ced5ae119e58db69467161a9f77ff45e1f9a384b
[utils] /
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 public class LinkStructure implements Listener {
34
35     private static final Log LOG = LogFactory.getLog(LinkStructure.class);
36
37     private String _monitorDir;
38
39     private File _linkDir;
40
41     private RecordingDatabase _database;
42
43     private SimpleDateFormat _format;
44     
45     private Map<File,Recording> _recordings; 
46
47     public LinkStructure(String aMonitorDir, File aLinkDir,
48             RecordingDatabase aDatabase) {
49         _monitorDir = aMonitorDir + "/";
50         deleteDir(aLinkDir);
51         _linkDir = aLinkDir;
52         _database = aDatabase;
53         _format = new SimpleDateFormat("yyyy-MM-dd-HH:mm");
54         _recordings = new HashMap<File,Recording>();
55     }
56     
57     private void deleteDir(File aFile) { 
58         for (File file: aFile.listFiles()) { 
59             if ( file.isDirectory()) { 
60                 deleteDir(file);   
61             }
62             LOG.info("File deleted " + file + ": " + file.delete()); 
63         }
64     }
65
66     /*
67      * (non-Javadoc)
68      * 
69      * @see org.wamblee.io.DirectoryMonitor.Listener#fileChanged(java.io.File)
70      */
71     public void fileChanged(File aFile) {
72         LOG.debug("file changed " + aFile);
73
74         // Re-assess file type
75         Recording recording = _recordings.get(aFile);
76         LOG.debug("Recording changed " + recording);
77         recording.setFilesize(aFile.length());
78         _database.update(recording);
79         String dir = getDirectory(recording);
80         FileType type = getFileType(aFile);
81         String path = dir + "/" + getFilename(recording, type);
82
83         if (exists(dir + "/" + getFilename(recording, type))) {
84             // Nothing to do.
85         } else {
86             mkdir(dir);
87             for (FileType t : FileType.values()) {
88                 rmlink(dir + "/" + getFilename(recording, t));
89             }
90             createSymLink(_monitorDir + aFile.getName(), dir + "/"
91                     + getFilename(recording, type));
92         }
93     }
94
95     /*
96      * (non-Javadoc)
97      * 
98      * @see org.wamblee.io.DirectoryMonitor.Listener#fileCreated(java.io.File)
99      */
100     public void fileCreated(File aFile) {
101         LOG.debug("file created " + aFile);
102         Recording recording = _database.findRecording(aFile.getName());
103         if ( recording == null ) { 
104             LOG.warn("Spurious recording which should not exist according to mythtv: " + aFile);
105             return; 
106         }
107         _recordings.put(aFile, recording);
108         LOG.info("New recording detected " + aFile + " "
109                 + recording);
110       
111         recording.setFilesize(aFile.length());
112         _database.update(recording);
113         String dir = getDirectory(recording);
114         mkdir(dir);
115         createSymLink(_monitorDir + aFile.getName(), dir + "/"
116                 + getFilename(recording, getFileType(aFile)));
117     }
118
119     /*
120      * (non-Javadoc)
121      * 
122      * @see org.wamblee.io.DirectoryMonitor.Listener#fileDeleted(java.io.File)
123      */
124     public void fileDeleted(File aFile) {
125         LOG.debug("file deleted " + aFile);
126         Recording recording = _recordings.get(aFile);
127         _recordings.remove(recording);
128         // mythtv will remove the recording from its database itself.
129         LOG.info("recording deleted " + recording);
130         String dir = getDirectory(recording);
131         for (FileType t: FileType.values()) {
132             rmlink(dir + "/" + getFilename(recording, t));
133         }
134         rmdir(dir);
135     }
136
137     private String getDirectory(Recording aRecording) {
138         return aRecording.getTitle().replaceAll("/", "-");
139     }
140
141     private FileType getFileType(File aFile) {
142         SimpleProcess process = new SimpleProcess(new File(_monitorDir), new String[] {
143                 "file", aFile.getName() });
144         try {
145             process.run();
146             if (process.getStdout().contains("RIFF")) {
147                 return FileType.AVI;
148             } else {
149                 return FileType.MPG;
150             }
151         } catch (IOException e) {
152             LOG.error("Determining filetype for " + aFile + " failed", e);
153             return FileType.MPG;
154         }
155     }
156
157     private String getFilename(Recording aRecording, FileType aType) {
158         return (_format.format(aRecording.getProgstart()) + "-"
159                 + aRecording.getSubtitle() + "-"
160                 + aRecording.getChannel().getName() + "."
161                 + aType.toString().toLowerCase()).replaceAll("/", "-");
162     }
163
164     private boolean exists(String aPath) {
165         LOG.debug("exists " + aPath);
166         return new File(_linkDir, aPath).exists();
167     }
168
169     private void rmlink(String aPath) {
170         LOG.debug("rmlink " + aPath);
171         File link = new File(_linkDir, aPath);
172         //if ( !link.exists()) { 
173         //    return; 
174        // }
175         if (!link.delete()) {
176             LOG.warn("Delete failed: " + aPath);
177         } else { 
178             LOG.info("Removed link " + link);
179         }
180     }
181
182     private void mkdir(String aDir) {
183         LOG.debug("mkdir " + aDir);
184         File dir = new File(_linkDir, aDir);
185         if ( dir.isDirectory()) { 
186             return; 
187         }
188         if (!dir.mkdirs()) {
189             LOG.warn("Could not create directory path: " + aDir);
190         } else { 
191             LOG.info("Created directory " + dir);
192         }
193     }
194
195     private void rmdir(String aDir) {
196         LOG.debug("rmdir " + aDir);
197         File dir = new File(_linkDir, aDir);
198         if (!dir.delete()) {
199             LOG.warn("Directory not deleted (still recordings left): " + aDir);
200         } else { 
201             LOG.info("Directory " + dir + " deleted.");
202         }
203     }
204
205     private void createSymLink(String aTarget, String aSource) {
206         try {
207             SimpleProcess process = new SimpleProcess(_linkDir, new String[] {
208                     "ln", "-s", aTarget, aSource });
209             process.run();
210             LOG.info("Created symlink " + aSource + " -> " + aTarget);
211         } catch (IOException e) {
212             LOG.error(
213                     "Could not create symlink: " + aTarget + " <- " + aSource,
214                     e);
215         }
216     }
217 }