Java tutorial
/* This file is part of Subsonic. Subsonic 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, either version 3 of the License, or (at your option) any later version. Subsonic 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 Subsonic. If not, see <http://www.gnu.org/licenses/>. Copyright 2009 (C) Sindre Mehus */ package net.sourceforge.subsonic.service; import net.sourceforge.subsonic.Logger; import net.sourceforge.subsonic.domain.MediaFile; import net.sourceforge.subsonic.domain.Playlist; import net.sourceforge.subsonic.util.FileUtil; import net.sourceforge.subsonic.util.StringUtil; import org.apache.commons.lang.StringEscapeUtils; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.Namespace; import org.jdom.input.SAXBuilder; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Provides services for loading and saving playlists to and from persistent storage. * * @author Sindre Mehus * @see Playlist */ public class PlaylistService { private static final Logger LOG = Logger.getLogger(PlaylistService.class); private SettingsService settingsService; private SecurityService securityService; private MediaFileService mediaFileService; /** * Saves the given playlist to persistent storage. * * @param playlist The playlist to save. * @throws IOException If an I/O error occurs. */ public void savePlaylist(Playlist playlist) throws IOException { String name = playlist.getName(); // Add m3u suffix if no other suitable suffix is given. if (!new PlaylistFilenameFilter().accept(getPlaylistDirectory(), name)) { name += ".m3u"; playlist.setName(name); } File playlistFile = new File(getPlaylistDirectory(), name); checkAccess(playlistFile); PrintWriter writer = new PrintWriter(playlistFile, StringUtil.ENCODING_UTF8); try { PlaylistFormat format = PlaylistFormat.getPlaylistFormat(playlistFile); format.savePlaylist(playlist, writer); } finally { writer.close(); } } /** * Loads a named playlist from persistent storage and into the provided playlist instance. * * @param playlist The playlist to populate. Any existing entries in the playlist will * be removed. * @param name The name of a previously persisted playlist. * @throws IOException If an I/O error occurs. */ public void loadPlaylist(Playlist playlist, String name) throws IOException { File playlistFile = new File(getPlaylistDirectory(), name); checkAccess(playlistFile); playlist.setName(name); BufferedReader reader = new BufferedReader( new InputStreamReader(new FileInputStream(playlistFile), StringUtil.ENCODING_UTF8)); try { PlaylistFormat format = PlaylistFormat.getPlaylistFormat(playlistFile); format.loadPlaylist(playlist, reader, mediaFileService); } finally { reader.close(); } } /** * Returns a list of all previously saved playlists. * * @return A list of all previously saved playlists. */ public File[] getSavedPlaylists() { return FileUtil.listFiles(getPlaylistDirectory(), new PlaylistFilenameFilter(), true); } /** * Returns the saved playlist with the given name. * * @param name The name of the playlist. * @return The playlist, or <code>null</code> if not found. */ public File getSavedPlaylist(String name) { for (File file : getSavedPlaylists()) { if (name.equals(file.getName())) { return file; } } return null; } /** * Deletes the named playlist from persistent storage. * * @param name The name of the playlist to delete. * @throws IOException If an I/O error occurs. */ public void deletePlaylist(String name) throws IOException { File file = new File(getPlaylistDirectory(), name); checkAccess(file); file.delete(); } /** * Returns the directory where playlists are stored. * * @return The directory where playlists are stored. */ public File getPlaylistDirectory() { return new File(settingsService.getPlaylistFolder()); } private void checkAccess(File file) { if (!securityService.isWriteAllowed(file)) { throw new SecurityException("Access denied to file " + file); } } public void setSettingsService(SettingsService settingsService) { this.settingsService = settingsService; } public void setSecurityService(SecurityService securityService) { this.securityService = securityService; } public void setMediaFileService(MediaFileService mediaFileService) { this.mediaFileService = mediaFileService; } private static class PlaylistFilenameFilter implements FilenameFilter { public boolean accept(File dir, String name) { name = name.toLowerCase(); return name.endsWith(".m3u") || name.endsWith(".pls") || name.endsWith(".xspf"); } } /** * Abstract superclass for playlist formats. */ private abstract static class PlaylistFormat { public abstract void loadPlaylist(Playlist playlist, BufferedReader reader, MediaFileService mediaFileService) throws IOException; public abstract void savePlaylist(Playlist playlist, PrintWriter writer) throws IOException; public static PlaylistFormat getPlaylistFormat(File file) { String name = file.getName().toLowerCase(); if (name.endsWith(".m3u")) { return new M3UFormat(); } if (name.endsWith(".pls")) { return new PLSFormat(); } if (name.endsWith(".xspf")) { return new XSPFFormat(); } return null; } } /** * Implementation of M3U playlist format. */ private static class M3UFormat extends PlaylistFormat { public void loadPlaylist(Playlist playlist, BufferedReader reader, MediaFileService mediaFileService) throws IOException { playlist.clear(); String line = reader.readLine(); while (line != null) { if (!line.startsWith("#")) { try { MediaFile file = mediaFileService.getMediaFile(line); if (file.getFile().exists()) { playlist.addFiles(true, file); } } catch (SecurityException x) { LOG.warn(x.getMessage(), x); } } line = reader.readLine(); } } public void savePlaylist(Playlist playlist, PrintWriter writer) throws IOException { writer.println("#EXTM3U"); for (MediaFile file : playlist.getFiles()) { writer.println(file.getPath()); } if (writer.checkError()) { throw new IOException("Error when writing playlist " + playlist.getName()); } } } /** * Implementation of PLS playlist format. */ private static class PLSFormat extends PlaylistFormat { public void loadPlaylist(Playlist playlist, BufferedReader reader, MediaFileService mediaFileService) throws IOException { playlist.clear(); Pattern pattern = Pattern.compile("^File\\d+=(.*)$"); String line = reader.readLine(); while (line != null) { Matcher matcher = pattern.matcher(line); if (matcher.find()) { try { MediaFile file = mediaFileService.getMediaFile(matcher.group(1)); if (file.getFile().exists()) { playlist.addFiles(true, file); } } catch (SecurityException x) { LOG.warn(x.getMessage(), x); } } line = reader.readLine(); } } public void savePlaylist(Playlist playlist, PrintWriter writer) throws IOException { writer.println("[playlist]"); int counter = 0; for (MediaFile file : playlist.getFiles()) { counter++; writer.println("File" + counter + '=' + file.getPath()); } writer.println("NumberOfEntries=" + counter); writer.println("Version=2"); if (writer.checkError()) { throw new IOException("Error when writing playlist " + playlist.getName()); } } } /** * Implementation of XSPF (http://www.xspf.org/) playlist format. */ private static class XSPFFormat extends PlaylistFormat { public void loadPlaylist(Playlist playlist, BufferedReader reader, MediaFileService mediaFileService) throws IOException { playlist.clear(); SAXBuilder builder = new SAXBuilder(); Document document; try { document = builder.build(reader); } catch (JDOMException x) { LOG.warn("Failed to parse XSPF playlist.", x); throw new IOException("Failed to parse XSPF playlist " + playlist.getName()); } Element root = document.getRootElement(); Namespace ns = root.getNamespace(); Element trackList = root.getChild("trackList", ns); List<?> tracks = trackList.getChildren("track", ns); for (Object obj : tracks) { Element track = (Element) obj; String location = track.getChildText("location", ns); if (location != null && location.startsWith("file://")) { location = location.replaceFirst("file://", ""); try { MediaFile file = mediaFileService.getMediaFile(location); if (file.getFile().exists()) { playlist.addFiles(true, file); } } catch (SecurityException x) { LOG.warn(x.getMessage(), x); } } } } public void savePlaylist(Playlist playlist, PrintWriter writer) throws IOException { writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); writer.println("<playlist version=\"1\" xmlns=\"http://xspf.org/ns/0/\">"); writer.println(" <trackList>"); for (MediaFile file : playlist.getFiles()) { writer.println(" <track><location>file://" + StringEscapeUtils.escapeXml(file.getPath()) + "</location></track>"); } writer.println(" </trackList>"); writer.println("</playlist>"); if (writer.checkError()) { throw new IOException("Error when writing playlist " + playlist.getName()); } } } }