Package com.aelitis.azureus.core.devices.impl

Source Code of com.aelitis.azureus.core.devices.impl.DeviceTivo

/*
* Created on Jul 24, 2009
* Created by Paul Gardner
*
* Copyright 2009 Vuze, Inc.  All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License only.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/


package com.aelitis.azureus.core.devices.impl;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.*;


import org.gudy.azureus2.core3.util.Constants;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.IndentWriter;
import org.gudy.azureus2.core3.xml.util.XUXmlWriter;
import org.gudy.azureus2.plugins.tracker.web.TrackerWebPageRequest;
import org.gudy.azureus2.plugins.tracker.web.TrackerWebPageResponse;
import org.gudy.azureus2.pluginsimpl.local.utils.FormattersImpl;

import com.aelitis.azureus.core.devices.TranscodeFile;

public class
DeviceTivo
  extends DeviceMediaRendererImpl
{
  private static final boolean  TRACE  = false;
 
  private static final String    NL        = "\r\n";
 
  private static Map<String,Comparator<ItemInfo>>  sort_comparators = new HashMap<String, Comparator<ItemInfo>>();
 
  static {
    sort_comparators.put(
      "Type",
      new Comparator<ItemInfo>()
      {
        public int
        compare(
          ItemInfo o1, ItemInfo o2 )
        {
          if ( o1.isContainer() == o2.isContainer()){
           
            return( 0 );
          }
         
          if ( o1.isContainer()){
           
            return( -1 );
          }
         
          return( 1 );
        }
      });
   
    sort_comparators.put(
      "Title",
      new Comparator<ItemInfo>()
      {
        Comparator<String> c = new FormattersImpl().getAlphanumericComparator( true );
       
        public int
        compare(
          ItemInfo o1, ItemInfo o2 )
        {
          return( c.compare( o1.getName(), o2.getName()));
        }
      });
   
    sort_comparators.put(
      "CreationDate",
      new Comparator<ItemInfo>()
      {
        public int
        compare(
          ItemInfo o1, ItemInfo o2 )
        {
          long  res = o1.getCreationMillis() - o2.getCreationMillis();
         
          if ( res < 0 ){
            return( -1 );
          }else if ( res > 0 ){
            return( 1 );
          }else{
            return( 0 );
          }
        }
      });
   
    sort_comparators.put( "LastChangeDate", sort_comparators.get( "CreationDate" ));
    sort_comparators.put( "CaptureDate", sort_comparators.get( "CreationDate" ));
  }
 
  private String    server_name;
  private boolean    tried_tcp_beacon;
 
  protected
  DeviceTivo(
    DeviceManagerImpl  _manager,
    String        _uid,
    String        _classification )
  {
    super( _manager, _uid, _classification, false );
   
    setName( "TiVo", true );
  }

  protected
  DeviceTivo(
    DeviceManagerImpl  _manager,
    Map          _map )
 
    throws IOException
  {
    super( _manager, _map );
  }
 
  protected boolean
  updateFrom(
    DeviceImpl    _other,
    boolean      _is_alive )
  {
    if ( !super.updateFrom( _other, _is_alive )){
     
      return( false );
    }
   
    if ( !( _other instanceof DeviceTivo )){
     
      Debug.out( "Inconsistent" );
     
      return( false );
    }
   
    DeviceTivo other = (DeviceTivo)_other;
   
    return( true );
  }
 
  protected void
  initialise()
  {
    super.initialise();
  }
 
  public boolean
  canFilterFilesView()
  {
    return( false );
  }
 
  public boolean
  canAssociate()
  {
    return( true );
  }
 
  public boolean
  canRestrictAccess()
  {
      // guess we could implement this one day
   
    return( false );
  }
 
  public boolean
  canShowCategories()
  {
    return( true );
  }
 
  protected boolean
  getShowCategoriesDefault()
  {
    return( true );
  }
 
  protected String
  getMachineName()
  {
    return( getPersistentStringProperty( PP_TIVO_MACHINE, null ));
  }
 
  protected void
  found(
    DeviceTivoManager  _tivo_manager,
    InetAddress      _address,
    String        _server_name,
    String        _machine )
  {
    boolean  first_time = false;
   
    synchronized( this ){
   
      if ( server_name == null ){
     
        server_name  = _server_name;
       
        first_time = true;
      }
    }
   
    if ( _machine == null && !tried_tcp_beacon ){
     
      try{
        Socket socket = new Socket();
     
        try{
          socket.connect( new InetSocketAddress( _address, 2190 ), 5000 );
         
          socket.setSoTimeout( 5000 );
 
          DataOutputStream dos = new DataOutputStream( socket.getOutputStream());
         
          byte[]  beacon_out = _tivo_manager.encodeBeacon( false, 0 );
         
          dos.writeInt( beacon_out.length );   
       
          dos.write( beacon_out );       
           
          DataInputStream dis = new DataInputStream( socket.getInputStream());
         
          int len = dis.readInt();
       
          if ( len < 65536 ){
           
            byte[] bytes = new byte[len];
 
            int  pos = 0;
           
            while( pos < len ){
           
              int read = dis.read( bytes, pos, len-pos );
             
              pos += read;
            }
           
            Map<String,String> beacon_in = _tivo_manager.decodeBeacon( bytes, len );
           
            _machine = beacon_in.get( "machine" );
         
        }finally{
         
          socket.close();
        }
      }catch( Throwable e ){
       
      }finally{
       
        tried_tcp_beacon = true;
      }
    }
   
    if ( _machine != null ){
     
      String existing = getMachineName();
     
      if ( existing == null || !existing.equals( _machine )){
       
        setPersistentStringProperty( PP_TIVO_MACHINE, _machine );
      }
    }
   
    setAddress( _address );
             
    alive();
   
    if ( first_time ){
     
      browseReceived();
    }
  }
 

  protected boolean
  generate(
    TrackerWebPageRequest   request,
    TrackerWebPageResponse   response )
 
    throws IOException
  {
    InetSocketAddress  local_address = request.getLocalAddress();
   
    if ( local_address == null ){
     
      return( false );
    }
   
    String  host = local_address.getAddress().getHostAddress();
   
    String  url = request.getURL();
   
    if ( TRACE ){
      System.out.println( "url: " + url );
    }
   
    if ( !url.startsWith( "/TiVoConnect?" )){
     
      return( false );
    }
   
    int pos = url.indexOf( '?' );
   
    if ( pos == -1 ){
     
      return(false );
    }
   
    String[]  bits = url.substring( pos+1 ).split( "&" );
   
    Map<String,String>  args = new HashMap<String, String>();
   
    for ( String bit: bits ){
     
      String[] x = bit.split( "=" );
     
      args.put( x[0], URLDecoder.decode( x[1], "UTF-8" ));
    }
   
    if ( TRACE ){
      System.out.println( "args: " + args );
    }
   
      // root folder /TiVoConnect?Command=QueryContainer&Container=%2F
   
    String  command = args.get( "Command" );
   
    if ( command == null ){
     
      return( false );
    }
   
    String reply = null;

    if ( command.equals( "QueryContainer" )){
     
      String  container = args.get( "Container" );
     
      if ( container == null ){
       
        return( false );
      }
           
      if ( container.equals( "/" )){
     
        reply =
        "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" + NL +
        "<TiVoContainer>" + NL +
        "    <Details>" + NL +
        "        <Title>" + server_name + "</Title>" + NL +
        "        <ContentType>x-container/tivo-server</ContentType>" + NL +
        "        <SourceFormat>x-container/folder</SourceFormat>" + NL +
        "        <TotalItems>1</TotalItems>" + NL +
        "    </Details>" + NL +
        "    <Item>" + NL +
        "        <Details>" + NL +
        "            <Title>" + server_name + "</Title>" + NL +
        "            <ContentType>x-container/tivo-videos</ContentType>" + NL +
        "            <SourceFormat>x-container/folder</SourceFormat>" + NL +
        "        </Details>" + NL +
        "        <Links>" + NL +
        "            <Content>" + NL +
        "                <Url>/TiVoConnect?Command=QueryContainer&amp;Container=" + urlencode( "/Content" ) + "</Url>" + NL +
        "                <ContentType>x-container/tivo-videos</ContentType>" + NL +
        "            </Content>" + NL +
        "        </Links>" + NL +
        "    </Item>" + NL +
        "    <ItemStart>0</ItemStart>" + NL +
        "    <ItemCount>1</ItemCount>" + NL +
        "</TiVoContainer>";
       
      }else if ( container.startsWith( "/Content" )){
       
        boolean  show_categories = getShowCategories();
       
        String  recurse = args.get( "Recurse" );
       
        if ( recurse != null && recurse.equals( "Yes" )){
         
          show_categories = false;
        }
       
        TranscodeFileImpl[] tfs = getFiles();
       
        String  category = null;
       
        Map<String,ContainerInfo>  categories = null;

        if ( show_categories ){
         
          if ( container.startsWith( "/Content/" )){
           
            category = container.substring( container.lastIndexOf( '/' ) + 1 );
           
          }else{
           
            categories = new HashMap<String,ContainerInfo>();
          }
        }
       
          // build list of applicable items
       
        List<ItemInfo> items = new ArrayList<ItemInfo>( tfs.length );
                 
        for ( TranscodeFileImpl file: tfs ){
           
          if ( !file.isComplete()){
           
              // see if we can set up a stream xcode for this but only if we
              // know the duration and the transcode is in progress (done in setup)
           
            if ( !setupStreamXCode( file )){
             
              continue;
            }
          }
                       
          if ( category != null ){
         
            boolean  hit = false;

            String[]  cats = file.getCategories();
           
            for ( String c: cats ){
             
              if ( c.equals( category )){
               
                hit = true;
              }
            }
           
            if ( !hit ){
             
              continue;
            }
          }
         
          FileInfo  info = new FileInfo( file, host );
         
          if ( info.isOK()){
           
            boolean  skip = false;
           
            if ( categories != null ){
             
              String[]  cats = file.getCategories();
             
              if ( cats.length > 0 ){
               
                skip = true;

                for ( String s: cats ){
                 
                  ContainerInfo cont = categories.get( s );
                 
                  if ( cont == null ){
                   
                    items.add( cont = new ContainerInfo( s ));
                   
                    categories.put( s, cont );
                  }
                             
                  cont.addChild();
                }
              }
            }
           
            if ( !skip ){
           
              items.add( info );
            }
          }
        }
               
          // sort
       
        String  sort_order = args.get( "SortOrder" );
       
        if ( sort_order != null ){
         
          String[] keys = Constants.PAT_SPLIT_COMMA.split(sort_order);
         
          final List<Comparator<ItemInfo>>   comparators = new  ArrayList<Comparator<ItemInfo>>();
          final List<Boolean>          reverses  = new ArrayList<Boolean>();
         
          for ( String key: keys ){
           
            boolean  reverse = false;
           
            if ( key.startsWith( "!" )){
             
              reverse = true;
             
              key = key.substring(1);
            }
           
            Comparator<ItemInfo> comp = sort_comparators.get( key );
           
            if ( comp != null ){
             
              comparators.add( comp );
              reverses.add( reverse );
            }
          }
         
          if ( comparators.size() > 0 ){
           
            Collections.sort(
              items,
              new Comparator<ItemInfo>()
              {
                public int
                compare(
                  ItemInfo i1,
                  ItemInfo i2)
                {
                  for ( int i=0;i<comparators.size();i++){
                   
                    Comparator<ItemInfo> comp = comparators.get(i);
                   
                    int res = comp.compare( i1, i2 );
                   
                    if ( res != 0 ){
                     
                      if ( reverses.get(i)){
                       
                        res = -res;
                      }
                     
                      return( res );
                    }
                  }
                 
                  return( 0 );
                }
              });
          }
        }
       
          // select items to return
       
        String  item_count    = args.get( "ItemCount" );
        String  anchor_offset   = args.get( "AnchorOffset" );
        String  anchor       = args.get( "AnchorItem" );
       
        int  num_items;
       
        if ( item_count == null ){
       
          num_items = items.size();
         
        }else{
         
            // can be negative if X items from end
         
          num_items = Integer.parseInt( item_count );
        }
       
        int  anchor_index;  // either one before or one after item to be returned depending on count +ve/-ve
       
        if ( num_items < 0 ){
         
          anchor_index = items.size();
         
        }else{
         
          anchor_index = -1;
        }
       
        if ( anchor != null ){
           
          for (int i=0;i<items.size();i++){
           
            ItemInfo info = items.get(i);
           
            if ( anchor.equals( info.getLinkURL())){
             
              anchor_index = i;
            }
          }
        }
       
        if ( anchor_offset != null ){
         
          anchor_index += Integer.parseInt( anchor_offset );
         
          if ( anchor_index < -1 ){
           
            anchor_index = -1;
           
          }else if ( anchor_index > items.size()){
           
            anchor_index = items.size();
          }
        }
       
        int  start_index;
        int end_index;
       
        if ( num_items > 0 ){
         
          start_index = anchor_index + 1;
         
          end_index  = anchor_index + num_items;
         
        }else{
         
          start_index = anchor_index + num_items;
         
          end_index  = anchor_index - 1;
        }

        if ( start_index < 0 ){
         
          start_index = 0;
        }
       
        if ( end_index >= items.size()){
         
          end_index = items.size() - 1;
        }
       
        int  num_to_return = end_index - start_index + 1;
       
        if ( num_to_return < 0 ){
         
          num_to_return = 0;
        }
               
        String machine = getMachineName();
       
        if ( machine == null ){
       
            // default until we find out what it is - can't see any way to get it apart from wait for broadcast
         
          machine = "TivoHDDVR";
        }
       
        String  header =
        "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" + NL +
        "<TiVoContainer>" + NL +
        "    <Tivos>" + NL +
        "      <Tivo>" + machine + "</Tivo>" + NL +
        "    </Tivos>" + NL +
        "    <ItemStart>" + start_index + "</ItemStart>" + NL +
        "    <ItemCount>" + num_to_return + "</ItemCount>" + NL +
        "    <Details>" + NL +
        "        <Title>" + escape( container ) + "</Title>" + NL +
        "        <ContentType>x-container/tivo-videos</ContentType>" + NL +
        "        <SourceFormat>x-container/folder</SourceFormat>" + NL +
        "        <TotalItems>" + items.size() + "</TotalItems>" + NL +
        "    </Details>" + NL;
       
        reply = header;
       
        for ( int i=start_index;i<=end_index;i++ ){
         
          ItemInfo  item = items.get(i);
         
          if ( item instanceof FileInfo ){
           
            FileInfo file = (FileInfo)item;
           
            long  file_size = file.getTargetSize();
           
            String  title   = escape( file.getName());
            String  desc  = title;
             
            int MAX_TITLE_LENGTH = 30;
           
            if ( title.length() > MAX_TITLE_LENGTH ){
             
                // TiVo has problems displaying a truncated title if it has
                // no spaces in it
             
              String   temp = "";
             
              for ( int j=0;j<title.length();j++){
               
                char c = title.charAt( j );
               
                if ( Character.isLetterOrDigit( c )){
                 
                  temp += c;
                }else{
                 
                  temp += ' ';
                }
              }
             
              int space_pos = temp.indexOf( ' ' );
             
              if ( space_pos == -1 || space_pos > MAX_TITLE_LENGTH ){
               
                temp = temp.substring( 0, 30 ) + "...";
              }
             
              title = temp;
            }
           
            reply +=
           
            "    <Item>" + NL +
            "        <Details>" + NL +
            "            <Title>" + title + "</Title>" + NL +
            "            <ContentType>video/x-tivo-mpeg</ContentType>" + NL +
            "            <SourceFormat>video/x-ms-wmv</SourceFormat>" + NL;
           
            if ( file_size > 0 ){
              reply +=
                "            <SourceSize>" + file_size + "</SourceSize>" + NL;
            }else{
              long est_size = file.getEstimatedTargetSize();
             
              if ( est_size > 0 ){
                reply +=
                  "            <SourceSize>" + est_size + "</SourceSize>" + NL;
              }
            }
           
            reply +=
            "            <Duration>" + file.getDurationMillis() + "</Duration>" + NL +
            "            <Description>" + desc + "</Description>" + NL +
            "            <SourceChannel>0</SourceChannel>" + NL +
            "            <SourceStation></SourceStation>" + NL +
            "            <SeriesId></SeriesId>" + NL +
            "            <CaptureDate>" + file.getCaptureDate() + "</CaptureDate>" + NL +
            "        </Details>" + NL +
            "        <Links>" + NL +
            "            <Content>" + NL +
            "                <ContentType>video/x-tivo-mpeg</ContentType>" + NL +
            "                    <AcceptsParams>No</AcceptsParams>" + NL +
            "                    <Url>" + file.getLinkURL() + "</Url>" + NL +
            "                </Content>" + NL +
            "                <CustomIcon>" + NL +
            "                    <ContentType>video/*</ContentType>" + NL +
            "                    <AcceptsParams>No</AcceptsParams>" + NL +
            "                    <Url>urn:tivo:image:save-until-i-delete-recording</Url>" + NL +
            "                </CustomIcon>" + NL +
            "        </Links>" + NL +
            "    </Item>" + NL;
           
          }else{
           
            ContainerInfo cont = (ContainerInfo)item;
           
            reply +=
            "    <Item>" + NL +
            "        <Details>" + NL +
            "            <Title>" + cont.getName() + "</Title>" + NL +
            "            <ContentType>x-container/tivo-videos</ContentType>" + NL +
            "            <SourceFormat>x-container/folder</SourceFormat>" + NL +
            "            <TotalItems>" + cont.getChildCount() + "</TotalItems>" + NL +
            "        </Details>" + NL +
            "        <Links>" + NL +
            "            <Content>" + NL +
            "                <Url>" + cont.getLinkURL() + "</Url>" + NL +
            "                <ContentType>x-container/tivo-videos</ContentType>" + NL +
            "            </Content>" + NL +
            "        </Links>" + NL +
            "    </Item>" + NL;
          }
        }
       
        String footer =
        "</TiVoContainer>";
       
        reply += footer;
      }
     
 
    }else if ( command.equals( "QueryFormats")){
   
      String source_format = args.get( "SourceFormat" );
     
      if ( source_format != null && source_format.startsWith( "video" )){
       
          // /TiVoConnect?Command=QueryFormats&SourceFormat=video%2Fx-tivo-mpeg
       
        reply =
        "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + NL +
        "<TiVoFormats><Format>" + NL +
        "<ContentType>video/x-tivo-mpeg</ContentType><Description/>" + NL +
        "</Format></TiVoFormats>";
      }
    }
   
    if ( reply == null ){
     
      return( false );
    }
   
    if ( TRACE ){
      System.out.println( "->" + reply );
    }
   
    response.setContentType( "text/xml" );
   
    response.getOutputStream().write( reply.getBytes( "UTF-8" ));
   
    return( true );
  }
 
  protected static String
  urlencode(
    String  str )
  {
    try{
      return( URLEncoder.encode( str, "UTF-8" ));
     
    }catch( Throwable e ){
     
      Debug.out( e );
     
      return( str );
    }
  }
 
  protected static String
  escape(
    String  str )
  {
    return( XUXmlWriter.escapeXML( str ));
  }
 
  protected void
  getDisplayProperties(
    List<String[]>  dp )
  {
    super.getDisplayProperties( dp );
   
    addDP( dp, "devices.tivo.machine", getMachineName());
  }
 
  public void
  generate(
    IndentWriter    writer )
  {
    super.generate( writer );
   
    try{
      writer.indent();
 
      writer.println( "tico_machine=" + getMachineName());
     
    }finally{
     
      writer.exdent();
    }
  }
 
  protected abstract static class
  ItemInfo
  {
    protected abstract String
    getName();
   
    protected abstract String
    getLinkURL();
   
    protected abstract boolean
    isContainer();
   
    public abstract long
    getCreationMillis();

  }
 
  protected static class
  ContainerInfo
    extends ItemInfo
  {
    private String    name;
   
    private int    child_count;
   
    protected
    ContainerInfo(
      String  _name )
    {
      name  = _name;
    }
   
    protected String
    getName()
    {
      return( name );
    }
   
    protected String
    getLinkURL()
    {
      return( "/TiVoConnect?Command=QueryContainer&amp;Container=" + urlencode( "/Content/" + name ));
    }
   
    protected void
    addChild()
    {
      child_count++;
    }
   
    protected int
    getChildCount()
    {
      return( child_count );
    }
   
    public long
    getCreationMillis()
    {
      return( 0 );
    }
   
    protected boolean
    isContainer()
    {
      return( true );
    }
  }
 
  protected static class
  FileInfo
    extends ItemInfo
  {
    private TranscodeFile  file;
    private String      stream_url;
    private long      target_size;
    private long      creation_millis;
   
    boolean  ok;
   
    protected
    FileInfo(
      TranscodeFile    _file,
      String        _host )
    {
      file  = _file;
     
      try{
        URL url = file.getStreamURL( _host );
       
        if ( url == null ){
         
          return;
        }
       
        stream_url = url.toExternalForm();
               
        try{
          if ( file.isComplete()){
           
            target_size = file.getTargetFile().getLength();
          }       
        }catch( Throwable e ){ 
        }
       
        creation_millis =  file.getCreationDateMillis();
       
        ok = true;
       
      }catch( Throwable e ){
       
      }
    }
   
    protected boolean
    isOK()
    {
      return( ok );
    }
   
    protected String
    getName()
    {
      return( file.getName());
    }
   
    protected String
    getLinkURL()
    {
      return( stream_url );
    }
   
    protected long
    getTargetSize()
    {
      return( target_size );
    }
   
    protected long
    getEstimatedTargetSize()
    {
        // TODO: we need access to max bitrate info... and then use duration and increase by, say, 5%
     
      try{
        long  duration_secs = getDurationMillis()/1000;
       
        if ( duration_secs == 0 ){
           
          long length = file.getSourceFile().getLength();
       
          return( length * 10 );
         
        }else{
          long mb_per_sec = 3// upper limit of 3 MB a sec assumed
           
          return( duration_secs * mb_per_sec*1024*1024L );
        }
      }catch( Throwable e ){
      }
       
      return( 0 );
    }
   
    protected long
    getDurationMillis()
    {
      return( file.getDurationMillis());
    }
   
    public long
    getCreationMillis()
    {
      return( creation_millis );
    }
   
    protected String
    getCaptureDate()
    {
      return( "0x" + Long.toString( creation_millis/1000, 16 ));
    }
   
    protected boolean
    isContainer()
    {
      return( false );
    }
  }
}
TOP

Related Classes of com.aelitis.azureus.core.devices.impl.DeviceTivo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.