4d3be3644009b30e9ae233c71d1c8b6a6a2aaf8e
[utils] / mythtv / monitor / src / main / java / org / wamblee / mythtv / LinkStructure.java
1 /*
2  * Copyright 2006 the original author or authors.
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * 
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  * 
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.wamblee.mythtv;
18
19 import java.io.File;
20 import java.io.IOException;
21 import java.text.SimpleDateFormat;
22 import java.util.HashMap;
23 import java.util.Map;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27 import org.wamblee.io.SimpleProcess;
28 import org.wamblee.io.DirectoryMonitor.Listener;
29
30 /**
31  * Link structure.
32  *
33  * @author Erik Brakkee
34  */
35 public class LinkStructure implements Listener {
36
37     private static final Log LOG = LogFactory.getLog(LinkStructure.class);
38
39     private String _monitorDir;
40
41     private File _linkDir;
42
43     private RecordingDatabase _database;
44
45     private SimpleDateFormat _format;
46     
47     private Map<File,Recording> _recordings; 
48
49     public LinkStructure(String aMonitorDir, File aLinkDir,
50             RecordingDatabase aDatabase) {
51         _monitorDir = aMonitorDir + "/";
52         deleteDir(aLinkDir);
53         _linkDir = aLinkDir;
54         _database = aDatabase;
55         _format = new SimpleDateFormat("yyyy-MM-dd-HH:mm");
56         _recordings = new HashMap<File,Recording>();
57     }
58     
59     private void deleteDir(File aFile) { 
60         for (File file: aFile.listFiles()) { 
61             if ( file.isDirectory()) { 
62                 deleteDir(file);   
63             }
64             LOG.info("File deleted " + file + ": " + file.delete()); 
65         }
66     }
67
68     /*
69      * (non-Javadoc)
70      * 
71      * @see org.wamblee.io.DirectoryMonitor.Listener#fileChanged(java.io.File)
72      */
73     public void fileChanged(File aFile) {
74         LOG.debug("file changed " + aFile);
75
76         // Re-assess file type
77         Recording recording = _recordings.get(aFile);
78         LOG.info("Recording changed " + recording);
79         recording.setFilesize(aFile.length());
80         _database.update(recording);
81         String dir = getDirectory(recording);
82         FileType type = getFileType(aFile);
83         String path = dir + "/" + getFilename(recording, type);
84
85         if (exists(dir + "/" + getFilename(recording, type))) {
86             // Nothing to do.
87         } else {
88             mkdir(dir);
89             for (FileType t : FileType.values()) {
90                 rmlink(dir + "/" + getFilename(recording, t));
91             }
92             createSymLink(_monitorDir + aFile.getName(), dir + "/"
93                     + getFilename(recording, type));
94         }
95     }
96
97     /*
98      * (non-Javadoc)
99      * 
100      * @see org.wamblee.io.DirectoryMonitor.Listener#fileCreated(java.io.File)
101      */
102     public void fileCreated(File aFile) {
103         LOG.debug("file created " + aFile);
104         Recording recording = _database.findRecording(aFile.getName());
105         if ( recording == null ) { 
106             LOG.warn("Spurious recording which should not exist according to mythtv: " + aFile);
107             return; 
108         }
109         _recordings.put(aFile, recording);
110         LOG.info("New recording detected " + aFile + " "
111                 + recording);
112       
113         recording.setFilesize(aFile.length());
114         _database.update(recording);
115         String dir = getDirectory(recording);
116         mkdir(dir);
117         createSymLink(_monitorDir + aFile.getName(), dir + "/"
118                 + getFilename(recording, getFileType(aFile)));
119     }
120
121     /*
122      * (non-Javadoc)
123      * 
124      * @see org.wamblee.io.DirectoryMonitor.Listener#fileDeleted(java.io.File)
125      */
126     public void fileDeleted(File aFile) {
127         LOG.debug("file deleted " + aFile);
128         Recording recording = _recordings.get(aFile);
129         _recordings.remove(recording);
130         // mythtv will remove the recording from its database itself.
131         LOG.info("recording deleted " + recording);
132         String dir = getDirectory(recording);
133         for (FileType t: FileType.values()) {
134             rmlink(dir + "/" + getFilename(recording, t));
135         }
136         rmdir(dir);
137     }
138
139     private String getDirectory(Recording aRecording) {
140         return aRecording.getTitle().replaceAll("/", "-");
141     }
142
143     private FileType getFileType(File aFile) {
144         SimpleProcess process = new SimpleProcess(new File(_monitorDir), new String[] {
145                 "file", aFile.getName() });
146         try {
147             process.run();
148             if (process.getStdout().contains("RIFF")) {
149                 return FileType.AVI;
150             } else {
151                 return FileType.MPG;
152             }
153         } catch (IOException e) {
154             LOG.error("Determining filetype for " + aFile + " failed", e);
155             return FileType.MPG;
156         }
157     }
158
159     private String getFilename(Recording aRecording, FileType aType) {
160         return (_format.format(aRecording.getProgstart()) + "-"
161                 + aRecording.getSubtitle() + "-"
162                 + aRecording.getChannel().getName() + "."
163                 + aType.toString().toLowerCase()).replaceAll("/", "-");
164     }
165
166     private boolean exists(String aPath) {
167         LOG.debug("exists " + aPath);
168         return new File(_linkDir, aPath).exists();
169     }
170
171     private void rmlink(String aPath) {
172         LOG.debug("rmlink " + aPath);
173         File link = new File(_linkDir, aPath);
174         //if ( !link.exists()) { 
175         //    return; 
176        // }
177         if (!link.delete()) {
178             LOG.warn("Delete failed: " + aPath);
179         } else { 
180             LOG.info("Removed link " + link);
181         }
182     }
183
184     private void mkdir(String aDir) {
185         LOG.debug("mkdir " + aDir);
186         File dir = new File(_linkDir, aDir);
187         if ( dir.isDirectory()) { 
188             return; 
189         }
190         if (!dir.mkdirs()) {
191             LOG.warn("Could not create directory path: " + aDir);
192         } else { 
193             LOG.info("Created directory " + dir);
194         }
195     }
196
197     private void rmdir(String aDir) {
198         LOG.debug("rmdir " + aDir);
199         File dir = new File(_linkDir, aDir);
200         if (!dir.delete()) {
201             LOG.warn("Directory not deleted (still recordings left): " + aDir);
202         } else { 
203             LOG.info("Directory " + dir + " deleted.");
204         }
205     }
206
207     private void createSymLink(String aTarget, String aSource) {
208         try {
209             SimpleProcess process = new SimpleProcess(_linkDir, new String[] {
210                     "ln", aTarget, aSource });
211             process.run();
212             LOG.info("Created symlink " + aSource + " -> " + aTarget);
213         } catch (IOException e) {
214             LOG.error(
215                     "Could not create symlink: " + aTarget + " <- " + aSource,
216                     e);
217         }
218     }
219 }