/* * Created on Jan 28, 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.IOException; import java.io.InputStream; import java.net.InetAddress; import java.net.URL; import java.text.SimpleDateFormat; import java.util.*; import org.gudy.azureus2.core3.config.COConfigurationManager; import org.gudy.azureus2.core3.util.Debug; import org.gudy.azureus2.core3.util.IndentWriter; import org.gudy.azureus2.core3.util.SystemTime; import org.gudy.azureus2.plugins.disk.DiskManagerFileInfo; import org.gudy.azureus2.plugins.download.Download; import org.gudy.azureus2.plugins.download.DownloadAttributeListener; import org.gudy.azureus2.plugins.download.DownloadManager; import org.gudy.azureus2.plugins.download.DownloadManagerListener; import org.gudy.azureus2.plugins.ipc.IPCInterface; import org.gudy.azureus2.plugins.torrent.Torrent; import org.gudy.azureus2.pluginsimpl.local.PluginCoreUtils; import org.gudy.azureus2.pluginsimpl.local.PluginInitializer; import org.gudy.azureus2.plugins.torrent.TorrentAttribute; import com.aelitis.azureus.core.AzureusCore; import com.aelitis.azureus.core.AzureusCoreRunningListener; import com.aelitis.azureus.core.AzureusCoreFactory; import com.aelitis.azureus.core.content.AzureusContentDownload; import com.aelitis.azureus.core.content.AzureusContentFile; import com.aelitis.azureus.core.content.AzureusPlatformContentDirectory; import com.aelitis.azureus.core.devices.TranscodeException; import com.aelitis.azureus.core.devices.TranscodeFile; import com.aelitis.azureus.core.devices.TranscodeJob; import com.aelitis.azureus.core.devices.TranscodeProfile; import com.aelitis.azureus.core.devices.TranscodeTarget; import com.aelitis.azureus.core.devices.TranscodeTargetListener; import com.aelitis.azureus.core.devices.DeviceManager.UnassociatedDevice; import com.aelitis.azureus.core.download.DiskManagerFileInfoStream; import com.aelitis.azureus.core.tag.Tag; import com.aelitis.azureus.core.tag.TagListener; import com.aelitis.azureus.core.tag.TagManagerFactory; import com.aelitis.azureus.core.tag.TagType; import com.aelitis.azureus.core.tag.Taggable; import com.aelitis.azureus.core.torrent.PlatformTorrentUtils; import com.aelitis.azureus.core.util.UUIDGenerator; import com.aelitis.azureus.util.PlayUtils; import com.aelitis.net.upnp.UPnPDevice; import com.aelitis.net.upnp.UPnPDeviceImage; import com.aelitis.net.upnp.UPnPRootDevice; public abstract class DeviceUPnPImpl extends DeviceImpl implements TranscodeTargetListener, DownloadManagerListener { private static final Object UPNPAV_FILE_KEY = new Object(); private static final Map<String,AzureusContentFile> acf_map = new WeakHashMap<String,AzureusContentFile>(); protected static String getDisplayName( UPnPDevice device ) { UPnPDevice root = device.getRootDevice().getDevice(); String fn = root.getFriendlyName(); if ( fn == null || fn.length() == 0 ){ fn = device.getFriendlyName(); } String dn = root.getModelName(); if ( dn == null || dn.length() == 0 ){ dn = device.getModelName(); } if ( dn != null && dn.length() > 0 ){ if ( !fn.contains( dn ) && ( !dn.contains( "Azureus" ) || dn.contains( "Vuze" ))){ fn += " (" + dn + ")"; } } return( fn ); } private final String MY_ACF_KEY; private final DeviceManagerUPnPImpl upnp_manager; private volatile UPnPDevice device_may_be_null; private IPCInterface upnpav_ipc; private TranscodeProfile dynamic_transcode_profile; private Map<String,AzureusContentFile> dynamic_xcode_map; protected DeviceUPnPImpl( DeviceManagerImpl _manager, UPnPDevice _device, int _type ) { super( _manager, _type, _type + "/" + _device.getRootDevice().getUSN(), getDisplayName( _device ), false ); upnp_manager = _manager.getUPnPManager(); setUPnPDevice(_device); MY_ACF_KEY = getACFKey(); } protected DeviceUPnPImpl( DeviceManagerImpl _manager, int _type, String _classification ) { super( _manager, _type, UUIDGenerator.generateUUIDString(), _classification, true ); upnp_manager = _manager.getUPnPManager(); MY_ACF_KEY = getACFKey(); } protected DeviceUPnPImpl( DeviceManagerImpl _manager, int _type, String _uuid, String _classification, boolean _manual, String _name ) { super( _manager, _type, _uuid==null?UUIDGenerator.generateUUIDString():_uuid, _classification, _manual, _name ); upnp_manager = _manager.getUPnPManager(); MY_ACF_KEY = getACFKey(); } protected DeviceUPnPImpl( DeviceManagerImpl _manager, int _type, String _uuid, String _classification, boolean _manual ) { super( _manager, _type, _uuid, _classification, _manual ); upnp_manager = _manager.getUPnPManager(); MY_ACF_KEY = getACFKey(); } protected DeviceUPnPImpl( DeviceManagerImpl _manager, Map _map ) throws IOException { super(_manager, _map ); upnp_manager = _manager.getUPnPManager(); MY_ACF_KEY = getACFKey(); } protected String getACFKey() { return( "DeviceUPnPImpl:device:" + getID()); } @Override protected boolean updateFrom( DeviceImpl _other, boolean _is_alive ) { if ( !super.updateFrom( _other, _is_alive )){ return( false ); } if ( !( _other instanceof DeviceUPnPImpl )){ Debug.out( "Inconsistent" ); return( false ); } DeviceUPnPImpl other = (DeviceUPnPImpl)_other; setUPnPDevice(other.device_may_be_null); return( true ); } @Override protected void initialise() { super.initialise(); } protected void UPnPInitialised() { } @Override protected void destroy() { super.destroy(); } protected DeviceManagerUPnPImpl getUPnPDeviceManager() { return( upnp_manager ); } protected UPnPDevice getUPnPDevice() { return( device_may_be_null ); } protected void setUPnPDevice( UPnPDevice device) { device_may_be_null = device; if (device != null) { // triggers any address change logic setAddress(getAddress()); } } public boolean isBrowsable() { return( true ); } public browseLocation[] getBrowseLocations() { List<browseLocation> locs = new ArrayList<browseLocation>(); UPnPDevice device = device_may_be_null; if ( device != null ){ URL presentation = getPresentationURL( device ); if ( presentation != null ){ locs.add( new browseLocationImpl( "device.upnp.present_url", presentation )); } int userMode = COConfigurationManager.getIntParameter("User Mode"); if ( userMode > 1 ){ locs.add( new browseLocationImpl( "device.upnp.desc_url", device.getRootDevice().getLocation())); } } return( locs.toArray( new browseLocation[ locs.size() ])); } public boolean canFilterFilesView() { return( true ); } public void setFilterFilesView( boolean filter ) { boolean existing = getFilterFilesView(); if ( existing != filter ){ setPersistentBooleanProperty( PP_FILTER_FILES, filter ); IPCInterface ipc = upnpav_ipc; if ( ipc != null ){ try{ ipc.invoke( "invalidateDirectory", new Object[]{}); }catch( Throwable e ){ } } } } public boolean getFilterFilesView() { return( getPersistentBooleanProperty( PP_FILTER_FILES, true )); } public boolean isLivenessDetectable() { return( true ); } protected URL getLocation() { UPnPDevice device = device_may_be_null; if ( device != null ){ UPnPRootDevice root = device.getRootDevice(); return( root.getLocation()); } return( null ); } public boolean canAssociate() { return( true ); } public void associate( UnassociatedDevice assoc ) { if ( isAlive()){ return; } setAddress( assoc.getAddress()); alive(); } public InetAddress getAddress() { try{ UPnPDevice device = device_may_be_null; if ( device != null ){ UPnPRootDevice root = device.getRootDevice(); URL location = root.getLocation(); return( InetAddress.getByName( location.getHost() )); }else{ InetAddress address = (InetAddress)getTransientProperty( TP_IP_ADDRESS ); if ( address != null ){ return( address ); } String last = getPersistentStringProperty( PP_IP_ADDRESS ); if ( last != null && last.length() > 0 ){ return( InetAddress.getByName( last )); } } }catch( Throwable e ){ Debug.printStackTrace(e); } return( null ); } public void setAddress( InetAddress address ) { setTransientProperty( TP_IP_ADDRESS, address ); setPersistentStringProperty( PP_IP_ADDRESS, address.getHostAddress()); } public boolean canRestrictAccess() { return( true ); } public String getAccessRestriction() { return( getPersistentStringProperty( PP_RESTRICT_ACCESS, "" )); } public void setAccessRestriction( String str ) { setPersistentStringProperty( PP_RESTRICT_ACCESS, str ); } protected URL getStreamURL( TranscodeFileImpl file ) { return( getStreamURL( file, null )); } protected URL getStreamURL( TranscodeFileImpl file, String host ) { browseReceived(); return( super.getStreamURL( file, host )); } protected String getMimeType( TranscodeFileImpl file ) { browseReceived(); return( super.getMimeType(file)); } protected void browseReceived() { IPCInterface ipc = upnp_manager.getUPnPAVIPC(); if ( ipc == null ){ return; } TranscodeProfile default_profile = getDefaultTranscodeProfile(); if ( default_profile == null ){ TranscodeProfile[] profiles = getTranscodeProfiles(); for ( TranscodeProfile p: profiles ){ if ( p.isStreamable()){ default_profile = p; break; } } } synchronized( this ){ if ( upnpav_ipc != null ){ return; } upnpav_ipc = ipc; if ( default_profile != null && default_profile.isStreamable()){ dynamic_transcode_profile = default_profile; } } if ( dynamic_transcode_profile != null && this instanceof TranscodeTarget ){ // In theory this is a plugin, so there should be a core.. // However, check just in case AzureusCoreFactory.addCoreRunningListener(new AzureusCoreRunningListener() { public void azureusCoreRunning(AzureusCore core) { DownloadManager dm = PluginInitializer.getDefaultInterface().getDownloadManager(); dm.addListener( DeviceUPnPImpl.this, true ); } }); } addListener( this ); TranscodeFile[] transcode_files = getFiles(); for ( TranscodeFile file: transcode_files ){ fileAdded( file, false ); } } protected void resetUPNPAV() { Set<String> to_remove = new HashSet<String>(); synchronized( this ){ if ( upnpav_ipc == null ){ return; } upnpav_ipc = null; dynamic_transcode_profile = null; dynamic_xcode_map = null; DownloadManager dm = PluginInitializer.getDefaultInterface().getDownloadManager(); dm.removeListener( this ); removeListener( this ); TranscodeFileImpl[] transcode_files = getFiles(); for ( TranscodeFileImpl file: transcode_files ){ file.setTransientProperty( UPNPAV_FILE_KEY, null ); to_remove.add( file.getKey()); } } synchronized( acf_map ){ for (String key: to_remove ){ acf_map.remove( key ); } } } public void downloadAdded( Download download ) { Torrent torrent = download.getTorrent(); if ( torrent != null && PlatformTorrentUtils.isContent( torrent, false )){ addDynamicXCode( download.getDiskManagerFileInfo()[0]); } } public void downloadRemoved( Download download ) { Torrent torrent = download.getTorrent(); if ( torrent != null && PlatformTorrentUtils.isContent( torrent, false )){ removeDynamicXCode( download.getDiskManagerFileInfo()[0]); } } protected void addDynamicXCode( final DiskManagerFileInfo source ) { final TranscodeProfile profile = dynamic_transcode_profile; IPCInterface ipc = upnpav_ipc; if ( profile == null || ipc == null ){ return; } try{ TranscodeFileImpl transcode_file = allocateFile( profile, false, source, false ); AzureusContentFile acf = (AzureusContentFile)transcode_file.getTransientProperty( UPNPAV_FILE_KEY ); if ( acf != null ){ return; } final String tf_key = transcode_file.getKey(); synchronized( acf_map ){ acf = acf_map.get( tf_key ); } if ( acf != null ){ return; } final DiskManagerFileInfo stream_file = new DiskManagerFileInfoStream( new DiskManagerFileInfoStream.StreamFactory() { private List<Object> current_requests = new ArrayList<Object>(); public StreamDetails getStream( Object request ) throws IOException { try{ TranscodeJobImpl job = getManager().getTranscodeManager().getQueue().add( (TranscodeTarget)DeviceUPnPImpl.this, profile, source, false, true, TranscodeTarget.TRANSCODE_UNKNOWN ); synchronized( this ){ current_requests.add( request ); } while( true ){ InputStream is = job.getStream( 1000 ); if ( is != null ){ return( new StreamWrapper( is, job )); } int state = job.getState(); if ( state == TranscodeJobImpl.ST_FAILED ){ throw( new IOException( "Transcode failed: " + job.getError())); }else if ( state == TranscodeJobImpl.ST_CANCELLED ){ throw( new IOException( "Transcode failed: job cancelled" )); }else if ( state == TranscodeJobImpl.ST_COMPLETE ){ throw( new IOException( "Job complete but no stream!" )); } synchronized( this ){ if ( !current_requests.contains( request )){ break; } } System.out.println( "waiting for stream" ); } IOException error = new IOException( "Stream request cancelled" ); job.failed( error ); throw( error ); }catch( IOException e ){ throw( e ); }catch( Throwable e ){ throw( new IOException( "Failed to add transcode job: " + Debug.getNestedExceptionMessage(e))); }finally{ synchronized( this ){ current_requests.remove( request ); } } } public void destroyed( Object request ) { synchronized( this ){ current_requests.remove( request ); } } }, transcode_file.getCacheFile()); acf = new AzureusContentFile() { public DiskManagerFileInfo getFile() { return( stream_file ); } public Object getProperty( String name ) { // TODO: duration etc if ( name.equals( MY_ACF_KEY )){ return( new Object[]{ DeviceUPnPImpl.this, tf_key }); }else if ( name.equals( PT_PERCENT_DONE )){ return( new Long(1000)); }else if ( name.equals( PT_ETA )){ return( new Long(0)); } return( null ); } }; synchronized( acf_map ){ acf_map.put( tf_key, acf ); } transcode_file.setTransientProperty( UPNPAV_FILE_KEY, acf ); syncCategoriesAndTags( transcode_file, true ); synchronized( this ){ if ( dynamic_xcode_map == null ){ dynamic_xcode_map = new HashMap<String,AzureusContentFile>(); } dynamic_xcode_map.put( tf_key, acf ); } ipc.invoke( "addContent", new Object[]{ acf }); }catch( Throwable e ){ Debug.out( e ); } } protected void removeDynamicXCode( final DiskManagerFileInfo source ) { final TranscodeProfile profile = dynamic_transcode_profile; IPCInterface ipc = upnpav_ipc; if ( profile == null || ipc == null ){ return; } try{ TranscodeFileImpl transcode_file = lookupFile( profile, source ); // if the file completed transcoding then we leave the result around for // the user to re-use if ( transcode_file != null && !transcode_file.isComplete()){ AzureusContentFile acf = null; synchronized( this ){ if ( dynamic_xcode_map != null ){ acf = dynamic_xcode_map.get( transcode_file.getKey()); } } transcode_file.delete( true ); if ( acf != null ){ ipc.invoke( "removeContent", new Object[]{ acf }); } synchronized( acf_map ){ acf_map.remove( transcode_file.getKey()); } } }catch( Throwable e ){ Debug.out( e ); } } protected boolean setupStreamXCode( TranscodeFileImpl transcode_file ) { final TranscodeJobImpl job = transcode_file.getJob(); if ( job == null ){ // may have just completed, say things are OK as caller can continue return( transcode_file.isComplete()); } final String tf_key = transcode_file.getKey(); AzureusContentFile acf; synchronized( acf_map ){ acf = acf_map.get( tf_key ); } if ( acf != null ){ return( true ); } IPCInterface ipc = upnpav_ipc; if ( ipc == null ){ return( false ); } if ( transcode_file.getDurationMillis() == 0 ){ return( false ); } try{ final DiskManagerFileInfo stream_file = new TranscodeJobOutputLeecher( job, transcode_file ); acf = new AzureusContentFile() { public DiskManagerFileInfo getFile() { return( stream_file ); } public Object getProperty( String name ) { // TODO: duration etc if ( name.equals( MY_ACF_KEY )){ return( new Object[]{ DeviceUPnPImpl.this, tf_key }); }else if ( name.equals( PT_PERCENT_DONE )){ return( new Long(1000)); }else if ( name.equals( PT_ETA )){ return( new Long(0)); } return( null ); } }; synchronized( acf_map ){ acf_map.put( tf_key, acf ); } ipc.invoke( "addContent", new Object[]{ acf }); log( "Set up stream-xcode for " + transcode_file.getName()); return( true ); }catch( Throwable e ){ return( false ); } } protected boolean isVisible( AzureusContentDownload file ) { if ( getFilterFilesView() || file == null ){ return false; } Download download = file.getDownload(); if ( download == null){ return false; } if ( download.isComplete()){ return true; } int numFiles = download.getDiskManagerFileCount(); for ( int i = 0; i < numFiles; i++){ DiskManagerFileInfo fileInfo = download.getDiskManagerFileInfo(i); if ( fileInfo == null || fileInfo.isDeleted() || fileInfo.isSkipped()){ continue; } if ( fileInfo.getLength() == fileInfo.getDownloaded()){ return true; }else if ( PlayUtils.canUseEMP( fileInfo )){ return( true ); } } return false; } protected boolean isVisible( AzureusContentFile file ) { if ( getFilterFilesView()){ Object[] x = (Object[])file.getProperty( MY_ACF_KEY ); if ( x != null && x[0] == this ){ String tf_key = (String)x[1]; return( getTranscodeFile( tf_key ) != null ); }else{ return( false ); } }else{ if ( file == null ){ return( false ); } DiskManagerFileInfo fileInfo = file.getFile(); if ( fileInfo == null || fileInfo.isDeleted() || fileInfo.isSkipped()){ return( false ); } if ( fileInfo.getLength() == fileInfo.getDownloaded()){ return( true ); }else if ( PlayUtils.canUseEMP( fileInfo )){ return( true ); } } return( false ); } public void fileAdded( TranscodeFile _transcode_file ) { fileAdded( _transcode_file, true ); } public void fileAdded( TranscodeFile _transcode_file, boolean _new_file ) { TranscodeFileImpl transcode_file = (TranscodeFileImpl)_transcode_file; IPCInterface ipc = upnpav_ipc; synchronized( this ){ if ( ipc == null ){ return; } if ( !transcode_file.isComplete()){ syncCategoriesAndTags( transcode_file, _new_file ); return; } AzureusContentFile acf = (AzureusContentFile)transcode_file.getTransientProperty( UPNPAV_FILE_KEY ); if ( acf != null ){ return; } final String tf_key = transcode_file.getKey(); synchronized( acf_map ){ acf = acf_map.get( tf_key ); } if ( acf != null ){ return; } try{ final DiskManagerFileInfo f = transcode_file.getTargetFile(); acf = new AzureusContentFile() { public DiskManagerFileInfo getFile() { return( f ); } public Object getProperty( String name ) { if( name.equals( MY_ACF_KEY )){ return( new Object[]{ DeviceUPnPImpl.this, tf_key }); }else if ( name.equals( PT_CATEGORIES )){ TranscodeFileImpl tf = getTranscodeFile( tf_key ); if ( tf != null ){ return( tf.getCategories()); } return( new String[0] ); }else if ( name.equals( PT_TAGS )){ TranscodeFileImpl tf = getTranscodeFile( tf_key ); if ( tf != null ){ return( tf.getTags( true )); } return( new String[0] ); } else if (name.equals(PT_TITLE)) { TranscodeFileImpl tf = getTranscodeFile( tf_key ); if ( tf != null ){ return( tf.getName()); } }else{ TranscodeFileImpl tf = getTranscodeFile( tf_key ); if ( tf != null ){ long res = 0; if ( name.equals( PT_DURATION )){ res = tf.getDurationMillis(); }else if ( name.equals( PT_VIDEO_WIDTH )){ res = tf.getVideoWidth(); }else if ( name.equals( PT_VIDEO_HEIGHT )){ res = tf.getVideoHeight(); }else if ( name.equals( PT_DATE )){ res = tf.getCreationDateMillis(); }else if ( name.equals( PT_PERCENT_DONE )){ if ( tf.isComplete()){ res = 1000; }else{ TranscodeJob job = tf.getJob(); if ( job == null ){ res = 0; }else{ res = 10*job.getPercentComplete(); } } return( res ); }else if ( name.equals( PT_ETA )){ if ( tf.isComplete()){ res = 0; }else{ TranscodeJob job = tf.getJob(); if ( job == null ){ res = Long.MAX_VALUE; }else{ res = job.getETASecs(); } } return( res ); } if ( res > 0 ){ return( new Long( res )); } } } return( null ); } }; transcode_file.setTransientProperty( UPNPAV_FILE_KEY, acf ); synchronized( acf_map ){ acf_map.put( tf_key, acf ); } syncCategoriesAndTags( transcode_file, _new_file ); try{ ipc.invoke( "addContent", new Object[]{ acf }); }catch( Throwable e ){ Debug.out( e ); } }catch( TranscodeException e ){ // file deleted } } } protected void syncCategoriesAndTags( TranscodeFileImpl tf, boolean inherit_from_download ) { try{ final Download dl = tf.getSourceFile().getDownload(); if ( dl != null ){ // only overwrite categories with the downloads ones if none already set if ( inherit_from_download ){ setCategories( tf, dl ); setTags( tf, dl ); } final String tf_key = tf.getKey(); dl.addAttributeListener( new DownloadAttributeListener() { public void attributeEventOccurred( Download download, TorrentAttribute attribute, int eventType) { TranscodeFileImpl tf = getTranscodeFile( tf_key ); if ( tf != null ){ setCategories( tf, download ); }else{ dl.removeAttributeListener( this, upnp_manager.getCategoryAttibute(), DownloadAttributeListener.WRITTEN ); } } }, upnp_manager.getCategoryAttibute(), DownloadAttributeListener.WRITTEN ); TagManagerFactory.getTagManager().getTagType( TagType.TT_DOWNLOAD_MANUAL ).addTagListener( PluginCoreUtils.unwrap( dl ), new TagListener() { public void taggableSync( Tag tag) { } public void taggableRemoved( Tag tag, Taggable tagged ) { update( tagged ); } public void taggableAdded( Tag tag, Taggable tagged ) { update( tagged ); } private void update( Taggable tagged ) { TranscodeFileImpl tf = getTranscodeFile( tf_key ); if ( tf != null ){ setTags( tf, dl ); }else{ TagManagerFactory.getTagManager().getTagType( TagType.TT_DOWNLOAD_MANUAL ).removeTagListener( tagged, this ); } } }); } }catch( Throwable e ){ } } protected void setCategories( TranscodeFileImpl tf, Download dl ) { String cat = dl.getCategoryName(); if ( cat != null && cat.length() > 0 && !cat.equals( "Categories.uncategorized" )){ tf.setCategories( new String[]{ cat }); }else{ tf.setCategories( new String[0] ); } } protected void setTags( TranscodeFileImpl tf, Download dl ) { List<Tag> tags = TagManagerFactory.getTagManager().getTagsForTaggable( PluginCoreUtils.unwrap( dl )); List<String> tag_names = new ArrayList<String>(); for ( Tag tag: tags ){ if ( tag.getTagType().getTagType() == TagType.TT_DOWNLOAD_MANUAL ){ tag_names.add( String.valueOf( tag.getTagUID())); } } tf.setTags( tag_names.toArray( new String[ tag_names.size()] )); } public void fileChanged( TranscodeFile file, int type, Object data ) { if ( file.isComplete()){ fileAdded( file, false ); } if ( type == TranscodeTargetListener.CT_PROPERTY ){ if ( data == TranscodeFile.PT_CATEGORY || data == TranscodeFile.PT_TAGS ){ AzureusContentFile acf; synchronized( acf_map ){ acf = acf_map.get(((TranscodeFileImpl)file).getKey()); } if ( acf != null ){ if ( data == TranscodeFile.PT_TAGS ){ AzureusPlatformContentDirectory.fireTagsChanged( acf ); }else{ AzureusPlatformContentDirectory.fireCatsChanged( acf ); } } } } } public void fileRemoved( TranscodeFile file ) { IPCInterface ipc = upnp_manager.getUPnPAVIPC(); if ( ipc == null ){ return; } synchronized( this ){ AzureusContentFile acf = (AzureusContentFile)file.getTransientProperty( UPNPAV_FILE_KEY ); if ( acf == null ){ return; } file.setTransientProperty( UPNPAV_FILE_KEY, null ); try{ ipc.invoke( "removeContent", new Object[]{ acf }); }catch( Throwable e ){ Debug.out( e ); } } synchronized( acf_map ){ acf_map.remove( ((TranscodeFileImpl)file).getKey()); } } protected URL getPresentationURL( UPnPDevice device ) { String presentation = device.getRootDevice().getDevice().getPresentation(); if ( presentation != null ){ try{ URL url = new URL( presentation ); return( url ); }catch( Throwable e ){ } } return( null ); } protected void getDisplayProperties( List<String[]> dp ) { super.getDisplayProperties( dp ); UPnPDevice device = device_may_be_null; if ( device != null ){ UPnPRootDevice root = device.getRootDevice(); URL location = root.getLocation(); addDP( dp, "dht.reseed.ip", location.getHost() + ":" + location.getPort()); String model_details = device.getModelName(); String model_url = device.getModelURL(); if ( model_url != null && model_url.length() > 0 ){ model_details += " (" + model_url + ")"; } String manu_details = device.getManufacturer(); String manu_url = device.getManufacturerURL(); if ( manu_url != null && manu_url.length() > 0 ){ manu_details += " (" + manu_url + ")"; } addDP( dp, "device.model.desc", device.getModelDescription()); addDP( dp, "device.model.name", model_details ); addDP( dp, "device.model.num", device.getModelNumber()); addDP( dp, "device.manu.desc", manu_details ); }else{ InetAddress ia = getAddress(); if ( ia != null ){ addDP( dp, "dht.reseed.ip", ia.getHostAddress()); } } addDP( dp, "!Is Liveness Detectable!", isLivenessDetectable()); if ( isManual() ){ addDP( dp, "azbuddy.ui.table.online", isAlive() ); addDP( dp, "device.lastseen", getLastSeen()==0?"":new SimpleDateFormat().format(new Date( getLastSeen() ))); } } public void generate( IndentWriter writer ) { super.generate( writer ); try{ writer.indent(); UPnPDevice device = device_may_be_null; if ( device == null ){ writer.println( "upnp_device=null" ); }else{ writer.println( "upnp_device=" + device.getFriendlyName()); } writer.println( "dyn_xcode=" + (dynamic_transcode_profile==null?"null":dynamic_transcode_profile.getName())); }finally{ writer.exdent(); } } protected static class StreamWrapper implements DiskManagerFileInfoStream.StreamFactory.StreamDetails { private InputStream is; private TranscodeJob job; protected StreamWrapper( InputStream _is, TranscodeJob _job ) { is = _is; job = _job; } public InputStream getStream() { return( is ); } public boolean hasFailed() { long start = SystemTime.getMonotonousTime(); while( true ){ int state = job.getState(); // timing issues - we can get here during teh fail process so // hang around a little if we're still running if ( state == TranscodeJobImpl.ST_RUNNING ){ if ( SystemTime.getMonotonousTime() - start > 5*1000 ){ return( true ); }else{ try{ Thread.sleep(250); continue; }catch( Throwable e ){ return( true ); } } } if ( state == TranscodeJobImpl.ST_FAILED || state == TranscodeJobImpl.ST_CANCELLED || state == TranscodeJobImpl.ST_REMOVED || state == TranscodeJobImpl.ST_STOPPED ){ // might have completed and then been removed TranscodeFile tf = job.getTranscodeFile(); if ( tf != null && tf.isComplete()){ return( false ); } return( true ); } } } } @Override public String getImageID() { String imageID = super.getImageID(); // commented out existing imageid check so upnp device image overrides if (/*imageID == null && */ device_may_be_null != null && isAlive()) { UPnPDeviceImage[] images = device_may_be_null.getImages(); if (images.length > 0) { URL location = getLocation(); if (location != null) { String url = "http://" + location.getHost() + ":" + location.getPort(); String imageUrl = images[0].getLocation(); for (UPnPDeviceImage imageInfo : images) { String mime = imageInfo.getLocation(); if (mime != null && mime.contains("png")) { imageUrl = imageInfo.getLocation(); break; } } if (!imageUrl.startsWith("/")) { url += "/"; } url += imageUrl; return url; } } } return imageID; } }