From: Erik Brakkee Date: Fri, 9 Feb 2007 23:03:12 +0000 (+0000) Subject: (no commit message) X-Git-Tag: wamblee-utils-0.7~881 X-Git-Url: http://wamblee.org/gitweb/?a=commitdiff_plain;h=06570e39b7e2c63d2f8094baf6658bd269610dcc;p=utils --- diff --git a/mythtv/src/main/java/org/wamblee/mythtv/Application.java b/mythtv/src/main/java/org/wamblee/mythtv/Application.java index d2135639..01d03026 100644 --- a/mythtv/src/main/java/org/wamblee/mythtv/Application.java +++ b/mythtv/src/main/java/org/wamblee/mythtv/Application.java @@ -12,7 +12,7 @@ * 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; @@ -21,27 +21,42 @@ import javax.servlet.ServletContextListener; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.quartz.SchedulerException; import org.wamblee.general.BeanKernel; /** - *f + * f */ public class Application implements ServletContextListener { private static final Log LOG = LogFactory.getLog(Application.class); - - /* (non-Javadoc) + + /* + * (non-Javadoc) + * * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent) */ public void contextInitialized(ServletContextEvent arg0) { - LOG.info("initializing"); - BeanKernel.getBeanFactory(); + LOG.info("initializing"); + try { + BeanKernel.getBeanFactory().find(MonitorScheduler.class) + .initialize(); + } catch (SchedulerException e) { + LOG.error("Error starting scheduler", e); + } } - - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent) */ public void contextDestroyed(ServletContextEvent arg0) { LOG.info("terminating"); + try { + BeanKernel.getBeanFactory().find(MonitorScheduler.class) + .shutdown(); + } catch (SchedulerException e) { + LOG.error("Error stopping scheduler", e); + } } } diff --git a/mythtv/src/main/java/org/wamblee/mythtv/Channel.java b/mythtv/src/main/java/org/wamblee/mythtv/Channel.java new file mode 100644 index 00000000..104e3aac --- /dev/null +++ b/mythtv/src/main/java/org/wamblee/mythtv/Channel.java @@ -0,0 +1,74 @@ +/* + * 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; + +/** + * + */ +public class Channel { + + private int _id; + + private String _name; + + protected Channel() { + // Empty + } + + /** + * @return the id + */ + public int getId() { + return _id; + } + + /** + * @return the name + */ + public String getName() { + return _name; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "Channel(" + _id + "," + _name + ")"; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object aObj) { + if ( !(aObj instanceof Channel)) { + return false; + } + Channel recording = (Channel)aObj; + return _id == recording._id; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return _id; + } + +} diff --git a/mythtv/src/main/java/org/wamblee/mythtv/FileType.java b/mythtv/src/main/java/org/wamblee/mythtv/FileType.java new file mode 100644 index 00000000..c27e2906 --- /dev/null +++ b/mythtv/src/main/java/org/wamblee/mythtv/FileType.java @@ -0,0 +1,25 @@ +/* + * 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; + +/** + * + */ +public enum FileType { + + MPG, AVI; +} diff --git a/mythtv/src/main/java/org/wamblee/mythtv/LinkStructure.java b/mythtv/src/main/java/org/wamblee/mythtv/LinkStructure.java new file mode 100644 index 00000000..868ab09d --- /dev/null +++ b/mythtv/src/main/java/org/wamblee/mythtv/LinkStructure.java @@ -0,0 +1,201 @@ +/* + * 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 = "/mnt/vcr"; + + private File _linkDir; + + private RecordingDatabase _database; + + private SimpleDateFormat _format; + + private Map _recordings; + + public LinkStructure(String aMonitorDir, File aLinkDir, + RecordingDatabase aDatabase) { + _monitorDir = aMonitorDir + "/"; + _linkDir = aLinkDir; + _database = aDatabase; + _format = new SimpleDateFormat("yyyy-MM-dd-HH:mm"); + _recordings = new HashMap(); + } + + /* + * (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); + 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); + 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(); + } + + 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(); + } + + 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); + } + } + +} diff --git a/mythtv/src/main/java/org/wamblee/mythtv/MonitorScheduler.java b/mythtv/src/main/java/org/wamblee/mythtv/MonitorScheduler.java new file mode 100644 index 00000000..321a1c23 --- /dev/null +++ b/mythtv/src/main/java/org/wamblee/mythtv/MonitorScheduler.java @@ -0,0 +1,67 @@ +/* + * 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.util.Date; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.quartz.JobDetail; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.SchedulerFactory; +import org.quartz.Trigger; +import org.quartz.TriggerUtils; +import org.quartz.impl.StdSchedulerFactory; + +/** + * + */ +public class MonitorScheduler { + + private static final Log LOG = LogFactory.getLog(MonitorScheduler.class); + private static final String JOB_NAME = "vcrmonitor"; + private static final String TRIGGER_NAME = "trigger"; + + private Scheduler _scheduler; + private int _intervalSeconds; + + public MonitorScheduler(int aInterval) throws SchedulerException { + SchedulerFactory schedulerFactory = new StdSchedulerFactory(); + _scheduler = schedulerFactory.getScheduler(); + _intervalSeconds = aInterval; + } + + public void initialize() throws SchedulerException { + LOG.info("Starting scheduler"); + _scheduler.start(); + + JobDetail jobDetail = new JobDetail(JOB_NAME, null, PollDirectoryJob.class); + Trigger trigger = TriggerUtils.makeSecondlyTrigger(_intervalSeconds); + //trigger.setStartTime(TriggerUtils.getEvenHourDate(new Date())); + trigger.setStartTime(new Date()); + trigger.setName(TRIGGER_NAME); + + _scheduler.scheduleJob(jobDetail, trigger); + } + + public void shutdown() throws SchedulerException { + LOG.info("Stopping scheduler"); + _scheduler.shutdown(); + } +} diff --git a/mythtv/src/main/java/org/wamblee/mythtv/MythtvHibernateMappings.java b/mythtv/src/main/java/org/wamblee/mythtv/MythtvHibernateMappings.java index b555fb52..481afe62 100644 --- a/mythtv/src/main/java/org/wamblee/mythtv/MythtvHibernateMappings.java +++ b/mythtv/src/main/java/org/wamblee/mythtv/MythtvHibernateMappings.java @@ -24,6 +24,6 @@ import org.wamblee.persistence.hibernate.HibernateMappingFiles; public class MythtvHibernateMappings extends HibernateMappingFiles { public MythtvHibernateMappings() { - // Empty + super(new String[] { "Channel.hbm.xml", "Recording.hbm.xml" }); } } diff --git a/mythtv/src/main/java/org/wamblee/mythtv/PollDirectoryJob.java b/mythtv/src/main/java/org/wamblee/mythtv/PollDirectoryJob.java new file mode 100644 index 00000000..e75ed119 --- /dev/null +++ b/mythtv/src/main/java/org/wamblee/mythtv/PollDirectoryJob.java @@ -0,0 +1,57 @@ +/* + * 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 org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.StatefulJob; +import org.wamblee.general.BeanKernel; +import org.wamblee.io.DirectoryMonitor; + +/** + * + */ +public class PollDirectoryJob implements StatefulJob { + + private static final Log LOG = LogFactory.getLog(PollDirectoryJob.class); + + public PollDirectoryJob() { + // Empty + } + + /* + * (non-Javadoc) + * + * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) + */ + public void execute(JobExecutionContext aContext) + throws JobExecutionException { + try { + DirectoryMonitor monitor = BeanKernel.getBeanFactory().find( + DirectoryMonitor.class); + monitor.poll(); + } catch (Throwable t) { + LOG + .error( + "something terrible happend, ignoring it and hoping for the best", + t); + } + } + +} diff --git a/mythtv/src/main/java/org/wamblee/mythtv/Recording.java b/mythtv/src/main/java/org/wamblee/mythtv/Recording.java new file mode 100644 index 00000000..cab43a6b --- /dev/null +++ b/mythtv/src/main/java/org/wamblee/mythtv/Recording.java @@ -0,0 +1,126 @@ +/* + * 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.Serializable; +import java.util.Date; + +/** + * + */ +public class Recording implements Serializable { + + private Channel _channel; + private Date _starttime; + + + private String _basename; + + private Date _progstart; + + private String _title; + + private String _subtitle; + + private long _filesize; + + protected Recording() { + // Empty + } + + /** + * @return the channel + */ + public Channel getChannel() { + return _channel; + } + + /** + * @return the basename + */ + public String getBasename() { + return _basename; + } + + /** + * @return the progstart + */ + public Date getStartTime() { + return _starttime; + } + + /** + * @return the progstart + */ + public Date getProgstart() { + return _progstart; + } + + /** + * @return the title + */ + public String getTitle() { + return _title; + } + + /** + * @return the subtitle + */ + public String getSubtitle() { + return _subtitle; + } + + /** + * @return the filesize + */ + public long getFilesize() { + return _filesize; + } + + public void setFilesize(long aFilesize) { + _filesize = aFilesize; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "Recording(" + _channel + "," + _basename + "," + _progstart + "," + _title + "," + _subtitle + ")"; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object aObj) { + if ( !(aObj instanceof Recording)) { + return false; + } + Recording recording = (Recording)aObj; + return _channel.equals(recording._channel) && _starttime.equals(recording._starttime); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return _channel.hashCode()*10 + ((int)_starttime.getTime() %10); + } + +} diff --git a/mythtv/src/main/java/org/wamblee/mythtv/RecordingDatabase.java b/mythtv/src/main/java/org/wamblee/mythtv/RecordingDatabase.java new file mode 100644 index 00000000..7ff51709 --- /dev/null +++ b/mythtv/src/main/java/org/wamblee/mythtv/RecordingDatabase.java @@ -0,0 +1,73 @@ +/* + * 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.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.HibernateException; +import org.hibernate.Session; +import org.hibernate.criterion.Expression; +import org.springframework.orm.hibernate3.HibernateCallback; +import org.springframework.orm.hibernate3.support.HibernateDaoSupport; + +/** + * + */ +public class RecordingDatabase extends HibernateDaoSupport { + + private static final Log LOG = LogFactory.getLog(RecordingDatabase.class); + + public RecordingDatabase() { + // Empty + } + + public void init() { + /* + for (Recording recording: (List)getHibernateTemplate().loadAll(Recording.class) ) { + LOG.info("Found recording " + recording); + } + LOG.info("After listing recordings"); + */ + } + + public Recording findRecording(final String aName) { + List result = (List) getHibernateTemplate().execute(new HibernateCallback() { + /* (non-Javadoc) + * @see org.springframework.orm.hibernate3.HibernateCallback#doInHibernate(org.hibernate.Session) + */ + public Object doInHibernate(Session aSession) throws HibernateException, SQLException { + return aSession.createCriteria(Recording.class).add(Expression.eq("_basename", aName)).list(); + } + }); + if ( result.size() > 1 ) { + throw new RuntimeException("More than two recordings returned"); + } + if ( result.size() == 0 ) { + return null; + } + return result.get(0); + } + + public void update(Recording aRecording) { + getHibernateTemplate().update(aRecording); + } +}