+/**+ * PS3 Media Server, for streaming any medias to your PS3.<br>+ * Copyright (C) 2008 A.Brochard+ *<p>+ * 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.+ *<p>+ * 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.+ *<p>+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.+ * + */ public class PMS {

+ /**getRootFolder returns the Root Folder for a given renderer. There could be the case+ * where a given media renderer needs a different root structure.+ * @param renderer {@link RendererConfiguration} is the renderer for which to get the RootFolder structure. If <b>null</b>, then+ * the default renderer is used.+ * @return {@link RootFolder} The root folder structure for a given renderer+ */ public RootFolder getRootFolder(RendererConfiguration renderer) { // something to do here for multiple directories views for each renderer if (renderer == null)@@ -139,7 +173,13 @@ return renderer.getRootFolder(); }

+ /**Executes a new Process and creates a fork that waits for its results. + * TODO:Extend explanation on where this is being used.+ * @param name Symbolic name for the process to be launched, only used in the trace log+ * @param error (boolean) Set to true if you want PMS to add error messages to the trace pane+ * @param params (array of Strings) array containing the command to call and its arguments+ * @return Returns true if the command exited as expected+ * @throws Exception TODO: Check which exceptions to use+ */ private boolean checkProcessExistence(String name, boolean error, String...params) throws Exception { PMS.info("launching: " + params[0]); //$NON-NLS-1$

+ /**Used to get the database. Needed in the case of the Xbox 360, that requires a database.+ * for its queries.+ * @return (DLNAMediaDatabase) If there exists a database register with the program, a pointer to it is returned.+ * If there is no database in memory, a new one is created. If the option to use a "cache" is deactivated, returns <b>null</b>.+ */ public synchronized DLNAMediaDatabase getDatabase() { if (PMS.configuration.getUseCache()) { if (database == null) {@@ -277,6 +379,11 @@ }

+ /**Initialisation procedure for PMS.+ * @return true if the server has been init correctly. false if the server could+ * not be set to listen to the UPNP port.+ * @throws Exception+ */ private boolean init () throws Exception {

+ /**Adds iPhoto folder. Used by manageRoot, so it is usually used as a folder at the+ * root folder. Only works when PMS is run on MacOsX.+ * TODO: Requirements for iPhoto.+ * @param renderer+ */ @SuppressWarnings("unchecked") public void addiPhotoFolder(RendererConfiguration renderer) { if (Platform.isMac()) {@@ -610,6 +734,14 @@ } }

+ /**Adds iTunes folder. Used by manageRoot, so it is usually used as a folder at the+ * root folder. Only works when PMS is run on MacOsX or Windows.<p>+ * The iTunes XML is parsed fully when this method is called, so it can take some time for+ * larger (+1000 albums) databases. TODO: Check if only music is being added.<P>+ * This method does not support genius playlists and does not provide a media library.+ * @param renderer {@link RendererConfiguration} Which media renderer to add this folder to.+ * @see PMS#manageRoot(RendererConfiguration)+ * @see PMS#getiTunesFile(boolean)+ */ @SuppressWarnings("unchecked") public void addiTunesFolder(RendererConfiguration renderer) { if (Platform.isMac() || Platform.isWindows()) {@@ -692,7 +833,12 @@ } } }- + /**Adds Video Settings folder. Used by manageRoot, so it is usually used as a folder at the+ * root folder. Child objects are created when this folder is created.+ * @param renderer {@link RendererConfiguration} Which media renderer to add this folder to.+ * @see PMS#manageRoot(RendererConfiguration)+ */+ public void addVideoSettingssFolder(RendererConfiguration renderer) { if (!PMS.configuration.getHideVideoSettings()) { VirtualFolder vf = new VirtualFolder(Messages.getString("PMS.37"), null); //$NON-NLS-1$@@ -787,6 +933,10 @@ } }

+ /**Returns the MediaLibrary used by PMS.+ * @return (MediaLibrary) Used library, if any. null if none is in use.+ */ public MediaLibrary getLibrary() { return library; }

+ /**Creates a new MediaLibrary object and adds the Media Library folder at root. This function+ * can only be run once, or the previous MediaLibrary object can be lost.<P>+ * @param renderer {@link RendererConfiguration} Which media renderer to add this folder to.+ * @return true if the settings allow to have a MediaLibrary.+ * @see PMS#manageRoot(RendererConfiguration)+ */ public boolean addMediaLibraryFolder(RendererConfiguration renderer) { if (PMS.configuration.getUseCache() && !renderer.isMediaLibraryAdded()) { library = new MediaLibrary();@@ -812,6 +971,11 @@ return false; }

+ /**Executes the needed commands in order to make PMS a Windows service that starts whenever the machine is started.+ * This function is called from the Network tab.+ * @return true if PMS could be installed as a Windows service.+ * @see NetworkTab#build()+ */ public boolean installWin32Service() { PMS.minimal(Messages.getString("PMS.41")); //$NON-NLS-1$ String cmdArray [] = new String[] { "win32/service/wrapper.exe", "-r", "wrapper.conf" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$@@ -825,6 +989,12 @@ return pwinstall.isSuccess(); }

+ /**Adds a message to the error stream. This is usually called by+ * statements that are in a try/catch block.+ * @param msg {@link String} to be added to the error stream+ * @param t {@link Throwable} comes from an {@link Exception} + */ public static void error(String msg, Throwable t) { if (instance != null) instance.message(msg, t); }

+ /**Print a message in a given channel. The channels can be following:+ * <ul><li>{@link #MINIMAL} for informative messages that the user need to be aware of. They are always+ * submitted to {@link System#out}, so they are shown to the command line window if one exists.+ * <li>{@link #INFO} for informative messages.+ * <li>{@link #DEBUG} for debug messages+ * </ul>+ * The debug level setting will state which ones of the three levels are being shown.+ * @param l {@link int} is the output text channel to use.+ * @param message {@link String} is the message to output+ */ private void message(int l, String message) {

String name = Thread.currentThread().getName();@@ -1014,6 +1238,10 @@

}

+ /**Handles the display of an error message. It is always logged and is shown in the command line window if available.+ * @param error {@link String} to be displayed+ * @param t {@link Throwable} from the {@link Exception} that has the error description.+ */ private void message(String error, Throwable t) {

+ /**Creates a new {@link #uuid} for the UPNP server to use. Tries to follow the RCFs for creating the UUID based on the link MAC address.+ * Defaults to a random one if that method is not available.+ * @return {@link String} with an Universally Unique Identifier.+ */ public String usn() { if (uuid == null) { boolean uuidBasedOnMAC = false;@@ -1082,6 +1317,9 @@ //return "uuid:1234567890TOTO::"; }

+/**Class Messages provides a mechanism to localise the text messages found in PMS. It is based+ * on {@link ResourceBundle}.+ */ public class Messages { private static final String BUNDLE_NAME = "net.pms.messages"; //$NON-NLS-1$

@@ -11,6 +14,12 @@ private Messages() { }

+ /**Returns the locale-specific string associated with the key.+ * @param key Keys in PMS follow the format "group.x". group states where this key is likely to be+ * used. For example, NetworkTab refers to the network configuration tab in the PMS GUI. x is just+ * a number. + * @return Descriptive string if key is found or a copy of the key string if it is not.+ */ public static String getString(String key) { try { return RESOURCE_BUNDLE.getString(key);

Another two fairly used files: DLNAResource and HTTPResource.Be aware that I am no developer myself, so I tried my best to add comments here. I might revisit these files whenever I finish with the rest (if I do...). If anybody knows better, please fix the comments with more accurate ones.

+ /**Returns parent object, usually a folder type of resource.+ * @return Parent object.+ * @see #parent+ */ public DLNAResource getParent() { return parent; } protected int specificType;+ /**+ * String representing this resource ID. This string is used by the UPNP ContentDirectory service.+ * There is no hard spec on the actual numbering except for the root container that always has to be "0".+ * In PMS, the format used is <i>number($number)+</i>. A common client that expects a given format,+ * that is different that the one used here, is the XBox360. For more info, check + * {@link http://www.mperfect.net/whsUpnp360/} . PMS translates the XBox360 queries on the fly.+ */ protected String id;+ /**+ * @return ID string+ * @see #id+ */ public String getId() { return id; }+ /**+ * List of children objects associated with this DLNAResource. This is only valid when the DLNAResource is of the container type.+ */ protected ArrayList<DLNAResource> children;+ /**+ * @return List of children objects+ * @see #children+ */ public ArrayList<DLNAResource> getChildren() { return children; }+ /**+ * In the DLDI queries, the UPNP server needs to give out the parent container where the item is. <i>parent</i> represents+ * such a container.+ */ protected DLNAResource parent;+ /**+ * @param parent Sets the parent folder.+ * @see #parent+ */ public void setParent(DLNAResource parent) { this.parent = parent; }@@ -89,11 +123,18 @@ return lastmodified; }

+ /**+ * Represents the transformation to be used to the file. If null, then + * @see Player+ */ protected Player player; //protected DLNAResource original; public Format getExt() { return ext; }+ /**Any {@link DLNAResource} needs to represent the container or item with a String.+ * @return String to be showed in the UPNP client.+ */ public abstract String getName(); public abstract String getSystemName(); public abstract long length();@@ -153,6 +194,14 @@ this.specificType = specificType; }

+ /** Recursive function that searchs through all of the children until it finds+ * a {@link DLNAResource} that matches the name.<p> Only used by {@link PMS#manageRoot(RendererConfiguration)}+ * while parsing the web.conf file.+ * @param name String to be compared the name to.+ * @return Returns a {@link DLNAResource} whose name matches the parameter name+ * @see #getName()+ * @see PMS#manageRoot(RendererConfiguration)+ */ public DLNAResource searchByName(String name) { for(DLNAResource child:children) { if (child.getName().equals(name))@@ -161,6 +210,11 @@ return null; }

+ /**Adds a new DLNAResource to the child list. Only useful if this object is of the container type.<P>+ * TODO: (botijo) check what happens with the child object. This function can and will transform the child+ * object. If the transcode option is set, the child item is converted to a container with the real+ * item and the transcode option folder. There is also a parser in order to get the right name and type,+ * I suppose. Is this the right place to be doing things like these? + * @param child DLNAResource to add to a container type.+ */ public void addChild(DLNAResource child) { //child.expert = expert; child.parent = this;@@ -314,6 +375,14 @@ } }

+ /**First thing it does it searches for an item matching the given objectID.+ * If children is false, then it returns the found object as the only object in the list.+ * If item or children have not been discovered already, then the {@link #closeChildren(int, boolean)} is called.<p>+ * TODO: (botijo) This function does a lot more than this!+ * @param objectId ID to search for.+ * @param children State if you want all the children in the returned list.+ * @param start+ * @param count+ * @param renderer Renderer for which to do the actions.+ * @return List of DLNAResource items. + * @throws IOException+ * @see #closeChildren(int, boolean)+ */ public synchronized ArrayList<DLNAResource> getDLNAResources(String objectId, boolean children, int start, int count, RendererConfiguration renderer) throws IOException { PMS.debug("Searching for objectId: " + objectId + " with children option: " +children); ArrayList<DLNAResource> resources = new ArrayList<DLNAResource>();@@ -413,6 +495,12 @@ return resources; }

+ /**Removes any children associated to this DLNAResource.<p>Note: (botijo) this function is not called anywhere.+ * + * @param level This integer represents how deep the items are in the tree. For the root+ * container (level==0), you do want to keep the children. For the rest of the levels,+ * children is set to null.+ * @see #children+ */+// @Deprecated public void reset(int level) { for(DLNAResource r:children) { r.reset(level++);@@ -598,11 +745,18 @@ children = null; }

+ /**+ * Prototype function. Original comment: need to override if some thumbnail works are to be done when mediaparserv2 enabled+ */ public void checkThumbnail() { // need to override if some thumbnail works are to be done when mediaparserv2 enabled }

+ /**Returns for a given item type the default MIME type associated. This is used in the HTTP transfers+ * as in the client might do different things for different MIME types.+ * @param type Type for which the default MIME type is needed.+ * @return Default MIME associated with the file type.+ */ public String getDefaultMimeType(int type) { String mimeType = HTTPResource.UNKNOWN_VIDEO_TYPEMIME; if (type == Format.VIDEO)@@ -80,6 +90,10 @@ }

+ /**Constructor for this class. The constructor does not add any child to the container. This is the only+ * chance to set the name of this container.+ * @param name String to be shown in the ContentBrowser service + * @param thumbnailIcon Represents a thumbnail to be shown. The String represents an absolute+ * path. Use null if none is available or desired.+ * @see #addChild(DLNAResource)+ */ public VirtualFolder(String name, String thumbnailIcon) { this.name = name; this.thumbnailIcon = thumbnailIcon;@@ -39,26 +49,45 @@ thumbnailContentType = HTTPResource.JPEG_TYPEMIME; }

+ /**Containers are likely not to be modified, so this one returns zero.+ * TODO: (botijo) When is this used then? Is this a prototype?+ * @return Zero+ */ public long lastModified() { return 0; }@@ -68,16 +97,26 @@ return getName(); }

+/**+ * Implements a container that when browsed, an action will be performed.+ * The class assumes that the action to be performed is to toggle a boolean value.+ * Because of this, the thumbnail is either a green tick mark or a red cross. Equivalent+ * videos are shown after the value is toggled.<p> + * However this is just cosmetic. Any action can be performed.+ */ public abstract class VirtualVideoAction extends DLNAResource {

private boolean enabled;@@ -38,6 +46,11 @@ private long timer1;

+ /**Constructor for this class. Recommended instantation includes overriding the {@link #enable()} function (example shown in the link).+ * @param name Name that is shown via the UPNP ContentBrowser service. This field cannot be changed after the instantiation.+ * @param enabled If true, a green tick mark is shown as thumbnail. If false, a red cross is shown. This initial value+ * is usually changed via the {@link #enable()} function.+ */ public VirtualVideoAction(String name, boolean enabled) { this.name = name; thumbnailContentType = HTTPResource.PNG_TYPEMIME;@@ -50,6 +63,14 @@ this.enabled = enabled; }

+ /**This function is called as an action from the UPNP client when+ * the user tries to play this item. This function calls instead the enable()+ * function in order to execute an action.+ * As the client expects to play an item, a really short video (less than 1s) is shown with + * the results of the action. + * @see #enable()+ * @see net.pms.dlna.DLNAResource#getInputStream()+ */ @Override public InputStream getInputStream() throws IOException { if (timer1 == -1)@@ -62,6 +83,21 @@ return getResourceInputStream(enabled?videoOk:videoKo); }

+ /**Prototype. This function is called by {@link #getInputStream()} and is the core of this class.+ * The main purpose of this function is toggle a boolean variable somewhere. + * The value of that boolean variable is shown then as either a green tick mark or a red cross.+ * However, this is just a cosmetic thing. Any Java code can be executed in this function, not only toggling a boolean variable.+ * Recommended way to instantiate this class is as follows (taken from {@link PMS#addVideoSettingssFolder(net.pms.configuration.RendererConfiguration)}:+ * <pre> VirtualFolder vf;+ * [...]+ * vf.addChild(new VirtualVideoAction(Messages.getString("PMS.3"), configuration.isMencoderNoOutOfSync()) { //$NON-NLS-1$+ * public boolean enable() {+ * configuration.setMencoderNoOutOfSync(!configuration.isMencoderNoOutOfSync());+ * return configuration.isMencoderNoOutOfSync();+ * }+ * }); </pre>+ * @return If true, a green tick mark is shown as thumbnail. If false, a red cross is shown.+ */ public abstract boolean enable();