From c49b68c8361e2475717da0e9ddd5bd46914e37b1 Mon Sep 17 00:00:00 2001 From: Reinhard Pointner Date: Tue, 2 Jun 2009 15:14:12 +0000 Subject: [PATCH] * added support for downloading subtitles from Sublight * added ZipArchive and RarArchive --- .../filebot/ui/panel/subtitle/Archive.java | 14 +++++ .../ui/panel/subtitle/ArchiveType.java | 49 ++++++++++++++--- .../filebot/ui/panel/subtitle/RarArchive.java | 53 +++++++++++++++++++ .../subtitle/SubtitleListCellRenderer.java | 2 +- .../ui/panel/subtitle/SubtitlePackage.java | 12 +++-- .../filebot/ui/panel/subtitle/ZipArchive.java | 52 ++++++++++++++++++ .../filebot/web/SublightSubtitleClient.java | 31 +++++++++-- .../web/SublightSubtitleDescriptor.java | 32 +++++++---- .../filebot/web/SublightVideoHasher.java | 34 ++++++------ .../filebot/web/SubtitleDescriptor.java | 6 ++- .../web/SublightSubtitleClientTest.java | 24 +++++++++ 11 files changed, 261 insertions(+), 48 deletions(-) create mode 100644 source/net/sourceforge/filebot/ui/panel/subtitle/Archive.java create mode 100644 source/net/sourceforge/filebot/ui/panel/subtitle/RarArchive.java create mode 100644 source/net/sourceforge/filebot/ui/panel/subtitle/ZipArchive.java diff --git a/source/net/sourceforge/filebot/ui/panel/subtitle/Archive.java b/source/net/sourceforge/filebot/ui/panel/subtitle/Archive.java new file mode 100644 index 00000000..d116c84e --- /dev/null +++ b/source/net/sourceforge/filebot/ui/panel/subtitle/Archive.java @@ -0,0 +1,14 @@ + +package net.sourceforge.filebot.ui.panel.subtitle; + + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Map; + + +interface Archive { + + Map extract() throws IOException; + +} diff --git a/source/net/sourceforge/filebot/ui/panel/subtitle/ArchiveType.java b/source/net/sourceforge/filebot/ui/panel/subtitle/ArchiveType.java index 462425a3..08603383 100644 --- a/source/net/sourceforge/filebot/ui/panel/subtitle/ArchiveType.java +++ b/source/net/sourceforge/filebot/ui/panel/subtitle/ArchiveType.java @@ -2,14 +2,46 @@ package net.sourceforge.filebot.ui.panel.subtitle; -public enum ArchiveType { - ZIP, - RAR, - UNKNOWN; - +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.Map; + + +enum ArchiveType { + ZIP { + + @Override + public Archive fromData(ByteBuffer data) { + return new ZipArchive(data); + } + }, + RAR { + + @Override + public Archive fromData(ByteBuffer data) { + return new RarArchive(data); + } + }, + UNDEFINED { + + @Override + public Archive fromData(ByteBuffer data) { + // cannot extract data, return empty archive + return new Archive() { + + @Override + public Map extract() throws IOException { + return Collections.emptyMap(); + } + }; + } + }; + + public static ArchiveType forName(String name) { if (name == null) - return UNKNOWN; + return UNDEFINED; if (name.equalsIgnoreCase("zip")) return ZIP; @@ -17,10 +49,13 @@ public enum ArchiveType { if (name.equalsIgnoreCase("rar")) return RAR; - return UNKNOWN; + return UNDEFINED; } + public abstract Archive fromData(ByteBuffer data); + + public String getExtension() { return toString().toLowerCase(); } diff --git a/source/net/sourceforge/filebot/ui/panel/subtitle/RarArchive.java b/source/net/sourceforge/filebot/ui/panel/subtitle/RarArchive.java new file mode 100644 index 00000000..55aa0575 --- /dev/null +++ b/source/net/sourceforge/filebot/ui/panel/subtitle/RarArchive.java @@ -0,0 +1,53 @@ + +package net.sourceforge.filebot.ui.panel.subtitle; + + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.LinkedHashMap; +import java.util.Map; + +import net.sourceforge.tuned.ByteBufferOutputStream; + +import de.innosystec.unrar.exception.RarException; +import de.innosystec.unrar.rarfile.FileHeader; + + +class RarArchive implements Archive { + + private final ByteBuffer data; + + + public RarArchive(ByteBuffer data) { + this.data = data.duplicate(); + } + + + public Map extract() throws IOException { + Map vfs = new LinkedHashMap(); + + try { + de.innosystec.unrar.Archive rar = new de.innosystec.unrar.Archive(data.duplicate()); + + for (FileHeader header : rar.getFileHeaders()) { + // ignore directory entries + if (header.isDirectory()) { + continue; + } + + ByteBufferOutputStream buffer = new ByteBufferOutputStream(header.getDataSize()); + + // write contents to buffer + rar.extractFile(header, buffer); + + // add memory file + vfs.put(header.getFileNameString(), buffer.getByteBuffer()); + } + } catch (RarException e) { + throw new IOException(e); + } + + return vfs; + } + +} diff --git a/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitleListCellRenderer.java b/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitleListCellRenderer.java index 27fd0104..9906174d 100644 --- a/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitleListCellRenderer.java +++ b/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitleListCellRenderer.java @@ -56,7 +56,7 @@ public class SubtitleListCellRenderer extends AbstractFancyListCellRenderer { //TODO download + progress progressBar.setVisible(false); - progressBar.setString(subtitle.getDownloadTask().getState().toString().toLowerCase()); + progressBar.setString(subtitle.getDownload().getState().toString().toLowerCase()); titleLabel.setForeground(isSelected ? list.getSelectionForeground() : list.getForeground()); languageLabel.setForeground(isSelected ? list.getSelectionForeground() : list.getForeground()); diff --git a/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePackage.java b/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePackage.java index d6167f85..1050e18e 100644 --- a/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePackage.java +++ b/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePackage.java @@ -2,13 +2,15 @@ package net.sourceforge.filebot.ui.panel.subtitle; +import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; +import javax.swing.SwingWorker; + import net.sourceforge.filebot.web.SubtitleDescriptor; -import net.sourceforge.tuned.DownloadTask; public class SubtitlePackage { @@ -17,14 +19,14 @@ public class SubtitlePackage { private final Language language; - private final DownloadTask downloadTask; + private final SwingWorker download; public SubtitlePackage(SubtitleDescriptor subtitleDescriptor) { this.subtitleDescriptor = subtitleDescriptor; this.language = new Language(languageCodeByName.get(subtitleDescriptor.getLanguageName()), subtitleDescriptor.getLanguageName()); - this.downloadTask = subtitleDescriptor.createDownloadTask(); + this.download = subtitleDescriptor.createDownloadTask(); } @@ -43,8 +45,8 @@ public class SubtitlePackage { } - public DownloadTask getDownloadTask() { - return downloadTask; + public SwingWorker getDownload() { + return download; } diff --git a/source/net/sourceforge/filebot/ui/panel/subtitle/ZipArchive.java b/source/net/sourceforge/filebot/ui/panel/subtitle/ZipArchive.java new file mode 100644 index 00000000..562c23eb --- /dev/null +++ b/source/net/sourceforge/filebot/ui/panel/subtitle/ZipArchive.java @@ -0,0 +1,52 @@ + +package net.sourceforge.filebot.ui.panel.subtitle; + + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import net.sourceforge.tuned.ByteBufferInputStream; +import net.sourceforge.tuned.ByteBufferOutputStream; + + +class ZipArchive implements Archive { + + private final ByteBuffer data; + + + public ZipArchive(ByteBuffer data) { + this.data = data.duplicate(); + } + + + public Map extract() throws IOException { + Map vfs = new LinkedHashMap(); + + // read first zip entry + ZipInputStream zipInputStream = new ZipInputStream(new ByteBufferInputStream(data.duplicate())); + ZipEntry zipEntry; + + try { + while ((zipEntry = zipInputStream.getNextEntry()) != null) { + ByteBufferOutputStream buffer = new ByteBufferOutputStream((int) zipEntry.getSize()); + ReadableByteChannel fileChannel = Channels.newChannel(zipInputStream); + + // write contents to buffer + while (buffer.transferFrom(fileChannel) >= 0); + + // add memory file + vfs.put(zipEntry.getName(), buffer.getByteBuffer()); + } + } finally { + zipInputStream.close(); + } + + return vfs; + } +} diff --git a/source/net/sourceforge/filebot/web/SublightSubtitleClient.java b/source/net/sourceforge/filebot/web/SublightSubtitleClient.java index f2677cd3..68f7ec1a 100644 --- a/source/net/sourceforge/filebot/web/SublightSubtitleClient.java +++ b/source/net/sourceforge/filebot/web/SublightSubtitleClient.java @@ -16,6 +16,8 @@ import javax.swing.Icon; import javax.xml.ws.Holder; import javax.xml.ws.WebServiceException; +import redstone.xmlrpc.util.Base64; + import net.sourceforge.filebot.ResourceManager; import net.sourceforge.tuned.Timer; import net.sublight.webservice.ArrayOfGenre; @@ -97,7 +99,7 @@ public class SublightSubtitleClient implements SubtitleProvider { // retrieve subtitles by name and year for (Subtitle subtitle : getSubtitleList(null, movie.getName(), movie.getYear(), languageName)) { - subtitles.add(new SublightSubtitleDescriptor(subtitle)); + subtitles.add(new SublightSubtitleDescriptor(subtitle, this)); } return subtitles; @@ -111,7 +113,7 @@ public class SublightSubtitleClient implements SubtitleProvider { for (Subtitle subtitle : getSubtitleList(SublightVideoHasher.computeHash(videoFile), null, null, languageName)) { // only keep linked subtitles if (subtitle.isIsLinked()) { - subtitles.add(new SublightSubtitleDescriptor(subtitle)); + subtitles.add(new SublightSubtitleDescriptor(subtitle, this)); } } @@ -186,11 +188,34 @@ public class SublightSubtitleClient implements SubtitleProvider { if (languageName.equalsIgnoreCase("Serbian")) return SubtitleLanguage.SERBIAN_LATIN; - // unkown language + // unknown language throw new IllegalArgumentException("Illegal language: " + languageName); } + protected byte[] getZipArchive(Subtitle subtitle) throws WebServiceException { + // require login + login(); + + Holder ticket = new Holder(); + Holder data = new Holder(); + Holder error = new Holder(); + + webservice.getDownloadTicket(session, null, subtitle.getSubtitleID(), null, ticket, null, error); + + // abort if something went wrong + checkError(error); + + webservice.downloadByID3(session, subtitle.getSubtitleID(), -1, false, ticket.value, null, data, error); + + // abort if something went wrong + checkError(error); + + // return zip file bytes + return Base64.decode(data.value.getBytes()); + } + + @Override public URI getSubtitleListLink(SearchResult searchResult, String languageName) { return null; diff --git a/source/net/sourceforge/filebot/web/SublightSubtitleDescriptor.java b/source/net/sourceforge/filebot/web/SublightSubtitleDescriptor.java index 4ec4f69d..4b7c8fa8 100644 --- a/source/net/sourceforge/filebot/web/SublightSubtitleDescriptor.java +++ b/source/net/sourceforge/filebot/web/SublightSubtitleDescriptor.java @@ -2,17 +2,22 @@ package net.sourceforge.filebot.web; -import net.sourceforge.tuned.DownloadTask; +import java.nio.ByteBuffer; + +import javax.swing.SwingWorker; + import net.sublight.webservice.Subtitle; public class SublightSubtitleDescriptor implements SubtitleDescriptor { private final Subtitle subtitle; + private final SublightSubtitleClient source; - public SublightSubtitleDescriptor(Subtitle subtitle) { + public SublightSubtitleDescriptor(Subtitle subtitle, SublightSubtitleClient source) { this.subtitle = subtitle; + this.source = source; } @@ -33,12 +38,6 @@ public class SublightSubtitleDescriptor implements SubtitleDescriptor { } - @Override - public String getArchiveType() { - return subtitle.getSubtitleType().value().toLowerCase(); - } - - @Override public String getLanguageName() { return subtitle.getLanguage().value(); @@ -46,9 +45,20 @@ public class SublightSubtitleDescriptor implements SubtitleDescriptor { @Override - public DownloadTask createDownloadTask() { - // TODO support - return new DownloadTask(null); + public String getArchiveType() { + return "zip"; + } + + + @Override + public SwingWorker createDownloadTask() { + return new SwingWorker() { + + @Override + protected ByteBuffer doInBackground() throws Exception { + return ByteBuffer.wrap(source.getZipArchive(subtitle)); + } + }; } diff --git a/source/net/sourceforge/filebot/web/SublightVideoHasher.java b/source/net/sourceforge/filebot/web/SublightVideoHasher.java index d674d00f..c68fd136 100644 --- a/source/net/sourceforge/filebot/web/SublightVideoHasher.java +++ b/source/net/sourceforge/filebot/web/SublightVideoHasher.java @@ -33,7 +33,7 @@ import net.sourceforge.filebot.mediainfo.MediaInfo.StreamKind; public final class SublightVideoHasher { - public static String computeHash(File file) throws IOException { + public static String computeHash(File file) throws IOException, LinkageError { byte[][] hash = new byte[4][]; // 1 byte = 0 (reserved) @@ -80,24 +80,20 @@ public final class SublightVideoHasher { } - protected static long getDuration(File file, TimeUnit unit) throws IOException { - try { - MediaInfo mediaInfo = new MediaInfo(); - - if (!mediaInfo.open(file)) - throw new IllegalArgumentException("Failed to open file: " + file); - - // get media info - String duration = mediaInfo.get(StreamKind.General, 0, "Duration"); - - // close handle - mediaInfo.close(); - - // convert from milliseconds to given unit - return unit.convert(Long.parseLong(duration), TimeUnit.MILLISECONDS); - } catch (Exception e) { - throw new IOException("Failed to get video duration", e); - } + protected static long getDuration(File file, TimeUnit unit) throws IOException, LinkageError { + MediaInfo mediaInfo = new MediaInfo(); + + if (!mediaInfo.open(file)) + throw new IOException("Failed to open file: " + file); + + // get media info + String duration = mediaInfo.get(StreamKind.General, 0, "Duration"); + + // close handle + mediaInfo.close(); + + // convert from milliseconds to given unit + return unit.convert(Long.parseLong(duration), TimeUnit.MILLISECONDS); } diff --git a/source/net/sourceforge/filebot/web/SubtitleDescriptor.java b/source/net/sourceforge/filebot/web/SubtitleDescriptor.java index f7ae5e01..b4af9cf4 100644 --- a/source/net/sourceforge/filebot/web/SubtitleDescriptor.java +++ b/source/net/sourceforge/filebot/web/SubtitleDescriptor.java @@ -2,7 +2,9 @@ package net.sourceforge.filebot.web; -import net.sourceforge.tuned.DownloadTask; +import java.nio.ByteBuffer; + +import javax.swing.SwingWorker; public interface SubtitleDescriptor { @@ -16,6 +18,6 @@ public interface SubtitleDescriptor { public String getArchiveType(); - public DownloadTask createDownloadTask(); + public SwingWorker createDownloadTask(); } diff --git a/test/net/sourceforge/filebot/web/SublightSubtitleClientTest.java b/test/net/sourceforge/filebot/web/SublightSubtitleClientTest.java index 140e6b22..16959130 100644 --- a/test/net/sourceforge/filebot/web/SublightSubtitleClientTest.java +++ b/test/net/sourceforge/filebot/web/SublightSubtitleClientTest.java @@ -4,7 +4,10 @@ package net.sourceforge.filebot.web; import static org.junit.Assert.*; +import java.io.ByteArrayInputStream; import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; import net.sublight.webservice.Subtitle; @@ -82,6 +85,27 @@ public class SublightSubtitleClientTest { } + @Test + public void getZipArchive() throws Exception { + Subtitle subtitle = new Subtitle(); + subtitle.setSubtitleID("1b4e9868-dded-49d0-b6e2-2d145328f6d4"); + + byte[] zip = client.getZipArchive(subtitle); + + // read first zip entry + ZipInputStream zipInputStream = new ZipInputStream(new ByteArrayInputStream(zip)); + + try { + ZipEntry entry = zipInputStream.getNextEntry(); + + assertEquals("Terminator The Sarah Connor Chronicles.srt", entry.getName()); + assertEquals(38959, entry.getSize(), 0); + } finally { + zipInputStream.close(); + } + } + + @AfterClass public static void logout() { // logout manually