/* * Copyright 2006 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.wamblee.mythtv; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.HashMap; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wamblee.io.SimpleProcess; import org.wamblee.io.DirectoryMonitor.Listener; /** * Link structure. */ public class LinkStructure implements Listener { private static final Log LOG = LogFactory.getLog(LinkStructure.class); private String _monitorDir; private File _linkDir; private RecordingDatabase _database; private SimpleDateFormat _format; private Map _recordings; public LinkStructure(String aMonitorDir, File aLinkDir, RecordingDatabase aDatabase) { _monitorDir = aMonitorDir + "/"; deleteDir(aLinkDir); _linkDir = aLinkDir; _database = aDatabase; _format = new SimpleDateFormat("yyyy-MM-dd-HH:mm"); _recordings = new HashMap(); } private void deleteDir(File aFile) { for (File file: aFile.listFiles()) { if ( file.isDirectory()) { deleteDir(file); } LOG.info("File deleted " + file + ": " + file.delete()); } } /* * (non-Javadoc) * * @see org.wamblee.io.DirectoryMonitor.Listener#fileChanged(java.io.File) */ public void fileChanged(File aFile) { LOG.debug("file changed " + aFile); // Re-assess file type Recording recording = _recordings.get(aFile); LOG.debug("Recording changed " + recording); recording.setFilesize(aFile.length()); _database.update(recording); String dir = getDirectory(recording); FileType type = getFileType(aFile); String path = dir + "/" + getFilename(recording, type); if (exists(dir + "/" + getFilename(recording, type))) { // Nothing to do. } else { mkdir(dir); for (FileType t : FileType.values()) { rmlink(dir + "/" + getFilename(recording, t)); } createSymLink(_monitorDir + aFile.getName(), dir + "/" + getFilename(recording, type)); } } /* * (non-Javadoc) * * @see org.wamblee.io.DirectoryMonitor.Listener#fileCreated(java.io.File) */ public void fileCreated(File aFile) { LOG.debug("file created " + aFile); Recording recording = _database.findRecording(aFile.getName()); _recordings.put(aFile, recording); LOG.info("New recording detected " + aFile + " " + recording); recording.setFilesize(aFile.length()); _database.update(recording); String dir = getDirectory(recording); mkdir(dir); createSymLink(_monitorDir + aFile.getName(), dir + "/" + getFilename(recording, getFileType(aFile))); } /* * (non-Javadoc) * * @see org.wamblee.io.DirectoryMonitor.Listener#fileDeleted(java.io.File) */ public void fileDeleted(File aFile) { LOG.debug("file deleted " + aFile); Recording recording = _recordings.get(aFile); _recordings.remove(recording); // mythtv will remove the recording from its database itself. LOG.info("recording deleted " + recording); String dir = getDirectory(recording); for (FileType t: FileType.values()) { rmlink(dir + "/" + getFilename(recording, t)); } rmdir(dir); } private String getDirectory(Recording aRecording) { return aRecording.getTitle().replaceAll("/", "-"); } private FileType getFileType(File aFile) { SimpleProcess process = new SimpleProcess(new File(_monitorDir), new String[] { "file", aFile.getName() }); try { process.run(); if (process.getStdout().contains("RIFF")) { return FileType.AVI; } else { return FileType.MPG; } } catch (IOException e) { LOG.error("Determining filetype for " + aFile + " failed", e); return FileType.MPG; } } private String getFilename(Recording aRecording, FileType aType) { return (_format.format(aRecording.getProgstart()) + "-" + aRecording.getSubtitle() + "-" + aRecording.getChannel().getName() + "." + aType.toString().toLowerCase()).replaceAll("/", "-"); } private boolean exists(String aPath) { LOG.debug("exists " + aPath); return new File(_linkDir, aPath).exists(); } private void rmlink(String aPath) { LOG.debug("rmlink " + aPath); File link = new File(_linkDir, aPath); //if ( !link.exists()) { // return; // } if (!link.delete()) { LOG.warn("Delete failed: " + aPath); } else { LOG.info("Removed link " + link); } } private void mkdir(String aDir) { LOG.debug("mkdir " + aDir); File dir = new File(_linkDir, aDir); if ( dir.isDirectory()) { return; } if (!dir.mkdirs()) { LOG.warn("Could not create directory path: " + aDir); } else { LOG.info("Created directory " + dir); } } private void rmdir(String aDir) { LOG.debug("rmdir " + aDir); File dir = new File(_linkDir, aDir); if (!dir.delete()) { LOG.warn("Directory not deleted (still recordings left): " + aDir); } else { LOG.info("Directory " + dir + " deleted."); } } private void createSymLink(String aTarget, String aSource) { try { SimpleProcess process = new SimpleProcess(_linkDir, new String[] { "ln", "-s", aTarget, aSource }); process.run(); LOG.info("Created symlink " + aSource + " -> " + aTarget); } catch (IOException e) { LOG.error( "Could not create symlink: " + aTarget + " <- " + aSource, e); } } }