+ support extracting archives (zip, rar, 7z, ...)
* added 7-Zip-JBinding libs and native dependencies * added CLI option -extract and make it available in scripting environment * allow --output to be used as output folder in -rename CLI call
This commit is contained in:
parent
374206480e
commit
cfccf90c72
83
build.xml
83
build.xml
@ -6,7 +6,9 @@
|
||||
<property name="title" value="${application.name}" />
|
||||
<property name="version" value="${application.version}" />
|
||||
|
||||
<tstamp><format property="today" pattern="yyyy-MM-dd" /></tstamp>
|
||||
<tstamp>
|
||||
<format property="today" pattern="yyyy-MM-dd" />
|
||||
</tstamp>
|
||||
|
||||
<!-- define source dirs -->
|
||||
<property name="dir.source" location="${basedir}/source" />
|
||||
@ -66,64 +68,64 @@
|
||||
<include name="org/apache/**" />
|
||||
<include name="org/w3c/dom/html/**" />
|
||||
</zipfileset>
|
||||
|
||||
|
||||
<zipfileset src="${dir.lib}/nekohtml.jar">
|
||||
<include name="org/cyberneko/html/**" />
|
||||
</zipfileset>
|
||||
|
||||
|
||||
<zipfileset src="${dir.lib}/json-simple.jar">
|
||||
<include name="org/json/simple/**" />
|
||||
</zipfileset>
|
||||
|
||||
|
||||
<zipfileset src="${dir.lib}/simmetrics.jar">
|
||||
<include name="uk/ac/shef/wit/simmetrics/**" />
|
||||
</zipfileset>
|
||||
|
||||
|
||||
<zipfileset src="${dir.lib}/glazedlists.jar">
|
||||
<include name="ca/odell/glazedlists/**" />
|
||||
</zipfileset>
|
||||
|
||||
|
||||
<zipfileset src="${dir.lib}/miglayout.jar">
|
||||
<include name="net/miginfocom/**" />
|
||||
</zipfileset>
|
||||
|
||||
|
||||
<zipfileset src="${dir.lib}/xmlrpc.jar">
|
||||
<include name="redstone/xmlrpc/**" />
|
||||
</zipfileset>
|
||||
|
||||
|
||||
<zipfileset src="${dir.lib}/args4j.jar">
|
||||
<include name="org/kohsuke/args4j/**" />
|
||||
</zipfileset>
|
||||
|
||||
|
||||
<zipfileset src="${dir.lib}/ehcache.jar">
|
||||
<include name="net/sf/ehcache/**" />
|
||||
<include name="ehcache-failsafe.xml" />
|
||||
<include name="ehcache-version.properties" />
|
||||
</zipfileset>
|
||||
|
||||
|
||||
<zipfileset src="${dir.lib}/slf4j.jar">
|
||||
<include name="org/slf4j/**" />
|
||||
</zipfileset>
|
||||
|
||||
|
||||
<zipfileset src="${dir.lib}/slf4j-jdk.jar">
|
||||
<include name="org/slf4j/**" />
|
||||
</zipfileset>
|
||||
|
||||
|
||||
<zipfileset src="${dir.lib}/commons-io.jar">
|
||||
<include name="org/apache/commons/io/**" />
|
||||
</zipfileset>
|
||||
|
||||
|
||||
<zipfileset src="${dir.lib}/jna.jar">
|
||||
<!-- include classes and native libraries -->
|
||||
<include name="com/sun/jna/**" />
|
||||
</zipfileset>
|
||||
|
||||
|
||||
<zipfileset src="${dir.lib}/groovy.jar">
|
||||
<include name="groovy*/**" />
|
||||
<include name="org/codehaus/groovy/**" />
|
||||
<include name="META-INF/dgminfo" />
|
||||
</zipfileset>
|
||||
|
||||
|
||||
<zipfileset src="${dir.lib}/icu4j.jar">
|
||||
<include name="com/ibm/icu/**" />
|
||||
</zipfileset>
|
||||
@ -131,11 +133,11 @@
|
||||
<zipfileset src="${dir.lib}/sublight-ws.jar">
|
||||
<include name="net/sublight/webservice/**" />
|
||||
</zipfileset>
|
||||
|
||||
|
||||
<zipfileset src="${dir.lib}/junrar-custom.jar">
|
||||
<include name="de/innosystec/unrar/**" />
|
||||
</zipfileset>
|
||||
|
||||
|
||||
<zipfileset src="${dir.lib}/jgat-custom.jar">
|
||||
<include name="com/dmurph/tracking/**" />
|
||||
</zipfileset>
|
||||
@ -146,16 +148,16 @@
|
||||
<target name="appbundle" depends="fatjar" description="Build an OSX application bundle">
|
||||
<taskdef name="jarbundler" classname="net.sourceforge.jarbundler.JarBundler" classpath="${dir.lib}/build/jarbundler.jar" />
|
||||
<copy tofile="${dir.dist}/appbundle/FileBot.jar" file="${path.fatjar}" />
|
||||
|
||||
|
||||
<!-- build app bundle folder and add native libs -->
|
||||
<jarbundler dir="${dir.dist}" name="${title}" version="${version}" build="${svn.revision}" icon="${dir.installer}/appbundle/icon.icns" bundleid="net.sourceforge.filebot" jar="${dir.dist}/appbundle/FileBot.jar" stubfile="${dir.installer}/appbundle/JavaApplicationStub" workingdirectory="$APP_PACKAGE/Contents/Resources/Java" mainclass="net.sourceforge.filebot.Main" jvmversion="1.6+" vmoptions="-Xmx256m">
|
||||
<javaproperty name="application.deployment" value="app"/>
|
||||
<javaproperty name="application.deployment" value="app" />
|
||||
</jarbundler>
|
||||
|
||||
|
||||
<!-- shell scripts -->
|
||||
<copy todir="${dir.dist}/${title}.app/Contents/MacOS" file="${dir.installer}/appbundle/filebot" />
|
||||
<copy todir="${dir.dist}/${title}.app/Contents/MacOS" file="${dir.installer}/appbundle/install.sh" />
|
||||
|
||||
|
||||
<copy todir="${dir.dist}/${title}.app/Contents/Resources/Java">
|
||||
<fileset dir="${dir.lib}/native/mac-x86_64" includes="*.dylib" />
|
||||
</copy>
|
||||
@ -163,14 +165,15 @@
|
||||
<!-- application bundle folder as .tar.gz -->
|
||||
<tar destfile="${path.appbundle.tar.gz}" compression="gzip">
|
||||
<tarfileset dir="${dir.dist}" includes="${title}.app/**" excludes="**/MacOS/**" />
|
||||
<tarfileset dir="${dir.dist}" includes="${title}.app/**/MacOS/**" filemode="755" /> <!-- IMPORTANT application stub must be executable!! -->
|
||||
<tarfileset dir="${dir.dist}" includes="${title}.app/**/MacOS/**" filemode="755" />
|
||||
<!-- IMPORTANT application stub must be executable!! -->
|
||||
</tar>
|
||||
</target>
|
||||
|
||||
|
||||
<target name="deb" depends="fatjar" description="Build debian package for i386 and amd64">
|
||||
<taskdef resource="ant_deb_task.properties" classpath="${dir.lib}/build/ant-deb.jar" />
|
||||
|
||||
|
||||
<antcall target="deb-arch">
|
||||
<param name="arch" value="i386" />
|
||||
</antcall>
|
||||
@ -204,31 +207,33 @@
|
||||
|
||||
<target name="msi-arch">
|
||||
<property name="mediainfo" location="${dir.lib}/native/win32-${arch}/MediaInfo.dll" />
|
||||
<property name="lib7z_binding" location="${dir.lib}/native/win32-${arch}/lib7z-JBinding.dll" />
|
||||
<property name="lib7z_gcc" location="${dir.lib}/native/win32-${arch}/lib7z-gcc.dll" />
|
||||
<property name="installer" location="${dir.dist}/FileBot_${version}_${arch}.msi" />
|
||||
|
||||
<exec executable="candle.exe" dir="${dir.installer}/msi" failonerror="true">
|
||||
<arg line="filebot-wix.xml -out ${dir.dist}/msi.wixobj -arch ${arch} -dreleaseversion=${version} -dfatjar=${path.fatjar} -dmediainfo=${mediainfo}" />
|
||||
<arg line="filebot-wix.xml -out ${dir.dist}/msi.wixobj -arch ${arch} -dreleaseversion=${version} -dfatjar=${path.fatjar} -dmediainfo=${mediainfo} -dlib7z_binding=${lib7z_binding} -dlib7z_gcc=${lib7z_gcc}" />
|
||||
</exec>
|
||||
<exec executable="light.exe" dir="${dir.installer}/msi" failonerror="true">
|
||||
<arg line="${dir.dist}/msi.wixobj -sval -ext WixUIExtension -out ${installer}" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
|
||||
|
||||
|
||||
<target name="portable" depends="fatjar" description="Portable application package">
|
||||
<mkdir dir="${dir.dist}/portable"/>
|
||||
<mkdir dir="${dir.dist}/portable" />
|
||||
<copy file="${path.fatjar}" tofile="${dir.dist}/portable/FileBot.jar" />
|
||||
<copy todir="${dir.dist}/portable">
|
||||
<fileset dir="${dir.installer}/portable" includes="*.exe, *.ini, *.sh" />
|
||||
</copy>
|
||||
|
||||
|
||||
<zip destfile="${dir.dist}/FileBot_${version}-portable.zip">
|
||||
<zipfileset dir="${dir.dist}/portable" includes="*.jar, *.exe, *.ini" />
|
||||
<zipfileset dir="${dir.dist}/portable" includes="*.sh" filemode="755" />
|
||||
</zip>
|
||||
</target>
|
||||
|
||||
|
||||
|
||||
|
||||
<target name="webstart" depends="jar" description="Build and compress jars used for webstart deployment">
|
||||
<!-- create dirs -->
|
||||
<mkdir dir="${dir.dist}/webstart" />
|
||||
@ -248,7 +253,7 @@
|
||||
<jar destfile="${dir.dist}/webstart/jna.jar">
|
||||
<zipfileset src="${dir.lib}/jna.jar" includes="**/*.class" />
|
||||
</jar>
|
||||
|
||||
|
||||
<!-- create mediainfo jar as seperate jar and use as trigger for lazy loading the native libs -->
|
||||
<jar destfile="${dir.dist}/webstart/mediainfo.jar">
|
||||
<fileset dir="${dir.build}" includes="net/sourceforge/filebot/mediainfo/**" />
|
||||
@ -292,7 +297,7 @@
|
||||
<signjar alias="filebot" keystore="filebot.keystore" storepass="secret">
|
||||
<fileset id="signjar" dir="${dir.dist}/webstart" includes="**/*.jar" />
|
||||
</signjar>
|
||||
|
||||
|
||||
<!-- pack200 all jars -->
|
||||
<apply executable="pack200" dest="${dir.dist}/webstart">
|
||||
<!-- workaround for bug 6575373, see http://bugs.sun.com/view_bug.do?bug_id=6575373 -->
|
||||
@ -383,11 +388,17 @@
|
||||
|
||||
<target name="svn-update">
|
||||
<typedef resource="org/tigris/subversion/svnant/svnantlib.xml">
|
||||
<classpath><fileset dir="${dir.lib}/build" includes="*.jar" /></classpath>
|
||||
<classpath>
|
||||
<fileset dir="${dir.lib}/build" includes="*.jar" />
|
||||
</classpath>
|
||||
</typedef>
|
||||
<svnSetting id="svn.settings" svnkit="true" javahl="false" />
|
||||
<svn refid="svn.settings"><update dir="${dir.source}" revision="HEAD" recurse="false" /></svn>
|
||||
<svn refid="svn.settings"><status path="${dir.source}" revisionProperty="svn.revision" /></svn>
|
||||
<svn refid="svn.settings">
|
||||
<update dir="${dir.source}" revision="HEAD" recurse="false" />
|
||||
</svn>
|
||||
<svn refid="svn.settings">
|
||||
<status path="${dir.source}" revisionProperty="svn.revision" />
|
||||
</svn>
|
||||
<echo>Revision: ${svn.revision}</echo>
|
||||
</target>
|
||||
|
||||
@ -419,7 +430,7 @@
|
||||
<scp todir="${sf.user}:${sf.password}@${deploy.release}" trust="yes" verbose="true">
|
||||
<fileset dir="${dir.dist}/release" includes="**/*.jar" />
|
||||
</scp>
|
||||
|
||||
|
||||
<!-- deploy portable application package -->
|
||||
<sleep seconds="5" />
|
||||
<scp todir="${sf.user}:${sf.password}@${deploy.release}" trust="yes" verbose="true">
|
||||
|
@ -1,2 +1,2 @@
|
||||
#!/bin/bash
|
||||
java -Dapplication.deployment=deb -Djna.library.path=/usr/share/filebot -Xmx256m -jar /usr/share/filebot/FileBot.jar "$@"
|
||||
java -Dapplication.deployment=deb -Djna.library.path=/usr/share/filebot -Djava.library.path=/usr/share/filebot -Xmx256m -jar /usr/share/filebot/FileBot.jar "$@"
|
||||
|
@ -32,6 +32,8 @@
|
||||
<Component Id='ApplicationBase' Guid='9E365344-A00C-45DE-A2A4-266412C3D06E'>
|
||||
<File Id='FileBot.jar' Name='FileBot.jar' Source='$(var.fatjar)' KeyPath='yes' />
|
||||
<File Id='MediaInfo.dll' Name='MediaInfo.dll' Source='$(var.mediainfo)' />
|
||||
<File Id='lib7z_JBinding.dll' Name='7z-JBinding.dll' Source='$(var.lib7z_binding)' />
|
||||
<File Id='lib7z_gcc.dll' Name='7z-gcc.dll' Source='$(var.lib7z_gcc)' />
|
||||
</Component>
|
||||
</Directory>
|
||||
</Directory>
|
||||
|
@ -4,4 +4,4 @@ while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
|
||||
dir_bin="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
||||
|
||||
# WARNING: NOT TESTED / HERE THERE BE DRAGONS
|
||||
java -Dapplication.deployment=portable "-Dapplication.dir=$dir_bin" "-Duser.home=$dir_bin" "-Djna.library.path=$dir_bin" -Djava.util.prefs.PreferencesFactory=net.sourceforge.tuned.prefs.FilePreferencesFactory -Dnet.sourceforge.tuned.prefs.file=prefs.properties -Xmx256m -jar "$dir_app/FileBot.jar" "$@"
|
||||
java -Dapplication.deployment=portable "-Dapplication.dir=$dir_bin" "-Duser.home=$dir_bin" "-Djna.library.path=$dir_bin" "-Djava.library.path=$dir_bin" -Djava.util.prefs.PreferencesFactory=net.sourceforge.tuned.prefs.FilePreferencesFactory -Dnet.sourceforge.tuned.prefs.file=prefs.properties -Xmx256m -jar "$dir_app/FileBot.jar" "$@"
|
||||
|
BIN
lib/native/linux-amd64/lib7z-JBinding.so
Normal file
BIN
lib/native/linux-amd64/lib7z-JBinding.so
Normal file
Binary file not shown.
BIN
lib/native/linux-i386/lib7z-JBinding.so
Normal file
BIN
lib/native/linux-i386/lib7z-JBinding.so
Normal file
Binary file not shown.
BIN
lib/native/mac-x86_64/lib7z-JBinding.dylib
Normal file
BIN
lib/native/mac-x86_64/lib7z-JBinding.dylib
Normal file
Binary file not shown.
BIN
lib/native/win32-x64/lib7z-JBinding.dll
Normal file
BIN
lib/native/win32-x64/lib7z-JBinding.dll
Normal file
Binary file not shown.
BIN
lib/native/win32-x64/lib7z-gcc.dll
Normal file
BIN
lib/native/win32-x64/lib7z-gcc.dll
Normal file
Binary file not shown.
BIN
lib/native/win32-x86/lib7z-JBinding.dll
Normal file
BIN
lib/native/win32-x86/lib7z-JBinding.dll
Normal file
Binary file not shown.
BIN
lib/native/win32-x86/lib7z-gcc.dll
Normal file
BIN
lib/native/win32-x86/lib7z-gcc.dll
Normal file
Binary file not shown.
BIN
lib/sevenzipjbinding.jar
Normal file
BIN
lib/sevenzipjbinding.jar
Normal file
Binary file not shown.
135
source/net/sourceforge/filebot/archive/Archive.java
Normal file
135
source/net/sourceforge/filebot/archive/Archive.java
Normal file
@ -0,0 +1,135 @@
|
||||
|
||||
package net.sourceforge.filebot.archive;
|
||||
|
||||
|
||||
import static org.apache.commons.io.FilenameUtils.*;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.sun.jna.Platform;
|
||||
|
||||
import net.sf.sevenzipjbinding.ArchiveFormat;
|
||||
import net.sf.sevenzipjbinding.IInStream;
|
||||
import net.sf.sevenzipjbinding.ISevenZipInArchive;
|
||||
import net.sf.sevenzipjbinding.PropID;
|
||||
import net.sf.sevenzipjbinding.SevenZip;
|
||||
import net.sf.sevenzipjbinding.SevenZipException;
|
||||
|
||||
|
||||
public class Archive implements Closeable {
|
||||
|
||||
static {
|
||||
// initialize 7z-JBinding native libs
|
||||
try {
|
||||
if (Platform.isWindows()) {
|
||||
System.loadLibrary("lib7z-gcc");
|
||||
}
|
||||
|
||||
System.loadLibrary("lib7z-JBinding");
|
||||
SevenZip.initLoadedLibraries();
|
||||
} catch (Throwable e) {
|
||||
Logger.getLogger(Archive.class.getName()).warning("Failed to load 7z-JBinding");
|
||||
}
|
||||
}
|
||||
|
||||
private ISevenZipInArchive inArchive;
|
||||
private Closeable openVolume;
|
||||
|
||||
|
||||
public Archive(File file) throws SevenZipException, IOException {
|
||||
if (!file.exists()) {
|
||||
throw new FileNotFoundException(file.getAbsolutePath());
|
||||
}
|
||||
|
||||
ArchiveOpenVolumeCallback openVolumeCallback = new ArchiveOpenVolumeCallback();
|
||||
IInStream inStream = openVolumeCallback.getStream(file.getAbsolutePath());
|
||||
|
||||
inArchive = SevenZip.openInArchive(null, inStream, openVolumeCallback);
|
||||
openVolume = openVolumeCallback;
|
||||
}
|
||||
|
||||
|
||||
public int itemCount() throws SevenZipException {
|
||||
return inArchive.getNumberOfItems();
|
||||
}
|
||||
|
||||
|
||||
public Map<PropID, Object> getItem(int index) throws SevenZipException {
|
||||
Map<PropID, Object> item = new EnumMap<PropID, Object>(PropID.class);
|
||||
|
||||
for (PropID prop : PropID.values()) {
|
||||
Object value = inArchive.getProperty(index, prop);
|
||||
if (value != null) {
|
||||
item.put(prop, value);
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
public List<File> listFiles() throws SevenZipException {
|
||||
List<File> paths = new ArrayList<File>();
|
||||
|
||||
for (int i = 0; i < inArchive.getNumberOfItems(); i++) {
|
||||
boolean isFolder = (Boolean) inArchive.getProperty(i, PropID.IS_FOLDER);
|
||||
if (!isFolder) {
|
||||
String path = (String) inArchive.getProperty(i, PropID.PATH);
|
||||
paths.add(new File(path));
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
|
||||
public void extract(ExtractOutProvider outputMapper) throws SevenZipException {
|
||||
inArchive.extract(null, false, new ExtractCallback(inArchive, outputMapper));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
inArchive.close();
|
||||
} catch (SevenZipException e) {
|
||||
throw new IOException(e);
|
||||
} finally {
|
||||
openVolume.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static final FileFilter VOLUME_ONE_FILTER = new FileFilter() {
|
||||
|
||||
private Pattern exclude = Pattern.compile("[.]r[0-9]+$|[.]part[0-9]*[2-9][.]rar$|[.][0-9]*[2-9]$", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
|
||||
@Override
|
||||
public boolean accept(File path) {
|
||||
if (exclude.matcher(path.getName()).find()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String ext = getExtension(path.getName());
|
||||
for (ArchiveFormat it : ArchiveFormat.values()) {
|
||||
if (it.getMethodName().equalsIgnoreCase(ext)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
|
||||
package net.sourceforge.filebot.archive;
|
||||
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import net.sf.sevenzipjbinding.*;
|
||||
import net.sf.sevenzipjbinding.impl.*;
|
||||
|
||||
|
||||
class ArchiveOpenVolumeCallback implements IArchiveOpenVolumeCallback, IArchiveOpenCallback, Closeable {
|
||||
|
||||
/**
|
||||
* Cache for opened file streams
|
||||
*/
|
||||
private Map<String, RandomAccessFile> openedRandomAccessFileList = new HashMap<String, RandomAccessFile>();
|
||||
|
||||
/**
|
||||
* Name of the last volume returned by {@link #getStream(String)}
|
||||
*/
|
||||
private String name;
|
||||
|
||||
|
||||
/**
|
||||
* This method should at least provide the name of the last
|
||||
* opened volume (propID=PropID.NAME).
|
||||
*
|
||||
* @see IArchiveOpenVolumeCallback#getProperty(PropID)
|
||||
*/
|
||||
public Object getProperty(PropID propID) throws SevenZipException {
|
||||
switch (propID) {
|
||||
case NAME:
|
||||
return name;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The name of the required volume will be calculated out of the
|
||||
* name of the first volume and a volume index. In case of RAR file,
|
||||
* the substring ".partNN." in the name of the volume file will
|
||||
* indicate a volume with id NN. For example:
|
||||
* <ul>
|
||||
* <li>test.rar - single part archive or multi-part archive with a single volume</li>
|
||||
* <li>test.part23.rar - 23-th part of a multi-part archive</li>
|
||||
* <li>test.part001.rar - first part of a multi-part archive. "00" indicates, that at least 100 volumes must exist.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public IInStream getStream(String filename) throws SevenZipException {
|
||||
try {
|
||||
// We use caching of opened streams, so check cache first
|
||||
RandomAccessFile randomAccessFile = openedRandomAccessFileList.get(filename);
|
||||
if (randomAccessFile != null) { // Cache hit.
|
||||
// Move the file pointer back to the beginning
|
||||
// in order to emulating new stream
|
||||
randomAccessFile.seek(0);
|
||||
|
||||
// Save current volume name in case getProperty() will be called
|
||||
name = filename;
|
||||
|
||||
return new RandomAccessFileInStream(randomAccessFile);
|
||||
}
|
||||
|
||||
// Nothing useful in cache. Open required volume.
|
||||
randomAccessFile = new RandomAccessFile(filename, "r");
|
||||
|
||||
// Put new stream in the cache
|
||||
openedRandomAccessFileList.put(filename, randomAccessFile);
|
||||
|
||||
// Save current volume name in case getProperty() will be called
|
||||
name = filename;
|
||||
return new RandomAccessFileInStream(randomAccessFile);
|
||||
} catch (FileNotFoundException fileNotFoundException) {
|
||||
// Required volume doesn't exist. This happens if the volume:
|
||||
// 1. never exists. 7-Zip doesn't know how many volumes should
|
||||
// exist, so it have to try each volume.
|
||||
// 2. should be there, but doesn't. This is an error case.
|
||||
|
||||
// Since normal and error cases are possible,
|
||||
// we can't throw an error message
|
||||
return null; // We return always null in this case
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Close all opened streams
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
for (RandomAccessFile file : openedRandomAccessFileList.values()) {
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setCompleted(Long files, Long bytes) throws SevenZipException {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setTotal(Long files, Long bytes) throws SevenZipException {
|
||||
}
|
||||
|
||||
}
|
85
source/net/sourceforge/filebot/archive/ExtractCallback.java
Normal file
85
source/net/sourceforge/filebot/archive/ExtractCallback.java
Normal file
@ -0,0 +1,85 @@
|
||||
|
||||
package net.sourceforge.filebot.archive;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import net.sf.sevenzipjbinding.ExtractAskMode;
|
||||
import net.sf.sevenzipjbinding.ExtractOperationResult;
|
||||
import net.sf.sevenzipjbinding.IArchiveExtractCallback;
|
||||
import net.sf.sevenzipjbinding.ISequentialOutStream;
|
||||
import net.sf.sevenzipjbinding.ISevenZipInArchive;
|
||||
import net.sf.sevenzipjbinding.PropID;
|
||||
import net.sf.sevenzipjbinding.SevenZipException;
|
||||
|
||||
|
||||
class ExtractCallback implements IArchiveExtractCallback {
|
||||
|
||||
private ISevenZipInArchive inArchive;
|
||||
private ExtractOutProvider extractOut;
|
||||
|
||||
private ExtractOutStream output = null;
|
||||
|
||||
|
||||
public ExtractCallback(ISevenZipInArchive inArchive, ExtractOutProvider extractOut) {
|
||||
this.inArchive = inArchive;
|
||||
this.extractOut = extractOut;
|
||||
}
|
||||
|
||||
|
||||
public ISequentialOutStream getStream(int index, ExtractAskMode extractAskMode) throws SevenZipException {
|
||||
if (extractAskMode != ExtractAskMode.EXTRACT) {
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean isFolder = (Boolean) inArchive.getProperty(index, PropID.IS_FOLDER);
|
||||
if (isFolder) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String path = (String) inArchive.getProperty(index, PropID.PATH);
|
||||
try {
|
||||
OutputStream target = extractOut.getStream(new File(path));
|
||||
if (target == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
output = new ExtractOutStream(target);
|
||||
return output;
|
||||
} catch (IOException e) {
|
||||
throw new SevenZipException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void prepareOperation(ExtractAskMode extractAskMode) throws SevenZipException {
|
||||
}
|
||||
|
||||
|
||||
public void setOperationResult(ExtractOperationResult extractOperationResult) throws SevenZipException {
|
||||
if (output != null) {
|
||||
try {
|
||||
output.close();
|
||||
} catch (IOException e) {
|
||||
throw new SevenZipException(e);
|
||||
} finally {
|
||||
output = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (extractOperationResult != ExtractOperationResult.OK) {
|
||||
throw new SevenZipException("Extraction Error: " + extractOperationResult);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setCompleted(long completeValue) throws SevenZipException {
|
||||
}
|
||||
|
||||
|
||||
public void setTotal(long total) throws SevenZipException {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
|
||||
package net.sourceforge.filebot.archive;
|
||||
|
||||
|
||||
import java.io.*;
|
||||
|
||||
|
||||
public interface ExtractOutProvider {
|
||||
|
||||
OutputStream getStream(File archivePath) throws IOException;
|
||||
|
||||
}
|
36
source/net/sourceforge/filebot/archive/ExtractOutStream.java
Normal file
36
source/net/sourceforge/filebot/archive/ExtractOutStream.java
Normal file
@ -0,0 +1,36 @@
|
||||
|
||||
package net.sourceforge.filebot.archive;
|
||||
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import net.sf.sevenzipjbinding.*;
|
||||
|
||||
|
||||
class ExtractOutStream implements ISequentialOutStream, Closeable {
|
||||
|
||||
private OutputStream out;
|
||||
|
||||
|
||||
public ExtractOutStream(OutputStream out) {
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int write(byte[] data) throws SevenZipException {
|
||||
try {
|
||||
out.write(data);
|
||||
} catch (IOException e) {
|
||||
throw new SevenZipException(e);
|
||||
}
|
||||
return data.length; // return amount of proceed data
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
out.close();
|
||||
}
|
||||
|
||||
}
|
40
source/net/sourceforge/filebot/archive/FileMapper.java
Normal file
40
source/net/sourceforge/filebot/archive/FileMapper.java
Normal file
@ -0,0 +1,40 @@
|
||||
|
||||
package net.sourceforge.filebot.archive;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
|
||||
public class FileMapper implements ExtractOutProvider {
|
||||
|
||||
private File outputDir;
|
||||
private boolean flatten;
|
||||
|
||||
|
||||
public FileMapper(File outputDir, boolean flatten) {
|
||||
this.outputDir = outputDir;
|
||||
this.flatten = flatten;
|
||||
};
|
||||
|
||||
|
||||
public File getOutputFile(File entry) {
|
||||
return new File(outputDir, flatten ? entry.getName() : entry.getPath());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public OutputStream getStream(File entry) throws IOException {
|
||||
File outputFile = getOutputFile(entry);
|
||||
File outputFolder = outputFile.getParentFile();
|
||||
|
||||
// create parent folder if necessary
|
||||
if (!outputFolder.isDirectory() && !outputFolder.mkdirs()) {
|
||||
throw new IOException("Failed to create folder: " + outputFolder);
|
||||
}
|
||||
|
||||
return new FileOutputStream(outputFile);
|
||||
}
|
||||
}
|
@ -52,10 +52,10 @@ public class ArgumentBean {
|
||||
@Option(name = "-check", usage = "Create/Check verification file", metaVar = "fileset")
|
||||
public boolean check;
|
||||
|
||||
@Option(name = "--output", usage = "Output options", metaVar = "[sfv, md5, sha1] or [srt]")
|
||||
@Option(name = "--output", usage = "Output path / format", metaVar = "Output options")
|
||||
public String output;
|
||||
|
||||
@Option(name = "--encoding", usage = "Character encoding", metaVar = "[UTF-8, windows-1252, GB18030, etc]")
|
||||
@Option(name = "--encoding", usage = "Output character encoding", metaVar = "[UTF-8, windows-1252, GB18030, etc]")
|
||||
public String encoding;
|
||||
|
||||
@Option(name = "-list", usage = "Fetch episode list")
|
||||
@ -64,6 +64,9 @@ public class ArgumentBean {
|
||||
@Option(name = "-mediainfo", usage = "Get media info")
|
||||
public boolean mediaInfo = false;
|
||||
|
||||
@Option(name = "-extract", usage = "Extract archives")
|
||||
public boolean extract = false;
|
||||
|
||||
@Option(name = "-script", usage = "Run Groovy script", metaVar = "robot.groovy")
|
||||
public String script = null;
|
||||
|
||||
@ -93,7 +96,7 @@ public class ArgumentBean {
|
||||
|
||||
|
||||
public boolean runCLI() {
|
||||
return rename || getSubtitles || getMissingSubtitles || check || list || mediaInfo || script != null;
|
||||
return rename || getSubtitles || getMissingSubtitles || check || list || mediaInfo || extract || script != null;
|
||||
}
|
||||
|
||||
|
||||
|
@ -8,8 +8,8 @@ import static net.sourceforge.tuned.FileUtilities.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.security.AccessController;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import javax.script.Bindings;
|
||||
@ -60,7 +60,11 @@ public class ArgumentProcessor {
|
||||
// execute CLI operations
|
||||
if (args.script == null) {
|
||||
// file operations
|
||||
Set<File> files = new LinkedHashSet<File>(args.getFiles(true));
|
||||
Collection<File> files = new LinkedHashSet<File>(args.getFiles(true));
|
||||
|
||||
if (args.extract) {
|
||||
files.addAll(cli.extract(files, args.output));
|
||||
}
|
||||
|
||||
if (args.getSubtitles) {
|
||||
files.addAll(cli.getSubtitles(files, args.query, args.lang, args.output, args.encoding, !args.nonStrict));
|
||||
@ -69,7 +73,7 @@ public class ArgumentProcessor {
|
||||
}
|
||||
|
||||
if (args.rename) {
|
||||
cli.rename(files, args.query, args.format, args.db, args.order, args.lang, !args.nonStrict);
|
||||
cli.rename(files, args.query, args.output, args.format, args.db, args.order, args.lang, !args.nonStrict);
|
||||
}
|
||||
|
||||
if (args.check) {
|
||||
|
@ -9,7 +9,7 @@ import java.util.List;
|
||||
|
||||
public interface CmdlineInterface {
|
||||
|
||||
List<File> rename(Collection<File> files, String query, String format, String db, String sortOrder, String lang, boolean strict) throws Exception;
|
||||
List<File> rename(Collection<File> files, String query, String output, String format, String db, String sortOrder, String lang, boolean strict) throws Exception;
|
||||
|
||||
|
||||
List<File> getSubtitles(Collection<File> files, String query, String lang, String output, String encoding, boolean strict) throws Exception;
|
||||
@ -29,4 +29,7 @@ public interface CmdlineInterface {
|
||||
|
||||
String getMediaInfo(File file, String format) throws Exception;
|
||||
|
||||
|
||||
List<File> extract(Collection<File> files, String output) throws Exception;
|
||||
|
||||
}
|
||||
|
@ -42,6 +42,8 @@ import java.util.concurrent.Future;
|
||||
import net.sourceforge.filebot.Analytics;
|
||||
import net.sourceforge.filebot.MediaTypes;
|
||||
import net.sourceforge.filebot.WebServices;
|
||||
import net.sourceforge.filebot.archive.Archive;
|
||||
import net.sourceforge.filebot.archive.FileMapper;
|
||||
import net.sourceforge.filebot.format.ExpressionFormat;
|
||||
import net.sourceforge.filebot.format.MediaBindingBean;
|
||||
import net.sourceforge.filebot.hash.HashType;
|
||||
@ -78,9 +80,10 @@ import net.sourceforge.tuned.FileUtilities.FolderFilter;
|
||||
public class CmdlineOperations implements CmdlineInterface {
|
||||
|
||||
@Override
|
||||
public List<File> rename(Collection<File> files, String query, String expression, String db, String sortOrder, String languageName, boolean strict) throws Exception {
|
||||
public List<File> rename(Collection<File> files, String query, String output, String expression, String db, String sortOrder, String languageName, boolean strict) throws Exception {
|
||||
ExpressionFormat format = (expression != null) ? new ExpressionFormat(expression) : null;
|
||||
Locale locale = getLanguage(languageName).toLocale();
|
||||
File outputDir = (output != null && output.length() > 0) ? new File(output) : null;
|
||||
|
||||
List<File> mediaFiles = filter(files, VIDEO_FILES, SUBTITLE_FILES);
|
||||
if (mediaFiles.isEmpty()) {
|
||||
@ -89,12 +92,12 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
|
||||
if (getEpisodeListProvider(db) != null) {
|
||||
// tv series mode
|
||||
return renameSeries(files, query, format, getEpisodeListProvider(db), SortOrder.forName(sortOrder), locale, strict);
|
||||
return renameSeries(files, query, outputDir, format, getEpisodeListProvider(db), SortOrder.forName(sortOrder), locale, strict);
|
||||
}
|
||||
|
||||
if (getMovieIdentificationService(db) != null) {
|
||||
// movie mode
|
||||
return renameMovie(files, query, format, getMovieIdentificationService(db), locale, strict);
|
||||
return renameMovie(files, query, outputDir, format, getMovieIdentificationService(db), locale, strict);
|
||||
}
|
||||
|
||||
// auto-determine mode
|
||||
@ -125,14 +128,14 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
|
||||
CLILogger.finest(format("Filename pattern: [%.02f] SxE, [%.02f] CWS", sxe / max, cws / max));
|
||||
if (sxe >= (max * 0.65) || cws >= (max * 0.65)) {
|
||||
return renameSeries(files, query, format, getEpisodeListProviders()[0], SortOrder.forName(sortOrder), locale, strict); // use default episode db
|
||||
return renameSeries(files, query, outputDir, format, getEpisodeListProviders()[0], SortOrder.forName(sortOrder), locale, strict); // use default episode db
|
||||
} else {
|
||||
return renameMovie(files, query, format, getMovieIdentificationServices()[0], locale, strict); // use default movie db
|
||||
return renameMovie(files, query, outputDir, format, getMovieIdentificationServices()[0], locale, strict); // use default movie db
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public List<File> renameSeries(Collection<File> files, String query, ExpressionFormat format, EpisodeListProvider db, SortOrder sortOrder, Locale locale, boolean strict) throws Exception {
|
||||
public List<File> renameSeries(Collection<File> files, String query, File outputDir, ExpressionFormat format, EpisodeListProvider db, SortOrder sortOrder, Locale locale, boolean strict) throws Exception {
|
||||
CLILogger.config(format("Rename episodes using [%s]", db.getName()));
|
||||
List<File> mediaFiles = filter(files, VIDEO_FILES, SUBTITLE_FILES);
|
||||
|
||||
@ -179,7 +182,7 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
File file = match.getValue();
|
||||
Episode episode = match.getCandidate();
|
||||
String newName = (format != null) ? format.format(new MediaBindingBean(episode, file)) : validateFileName(EpisodeFormat.SeasonEpisode.format(episode));
|
||||
File newFile = new File(newName + "." + getExtension(file));
|
||||
File newFile = new File(outputDir, newName + "." + getExtension(file));
|
||||
|
||||
if (isInvalidFilePath(newFile)) {
|
||||
CLILogger.config("Stripping invalid characters from new name: " + newName);
|
||||
@ -264,7 +267,7 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
}
|
||||
|
||||
|
||||
public List<File> renameMovie(Collection<File> files, String query, ExpressionFormat format, MovieIdentificationService service, Locale locale, boolean strict) throws Exception {
|
||||
public List<File> renameMovie(Collection<File> files, String query, File outputDir, ExpressionFormat format, MovieIdentificationService service, Locale locale, boolean strict) throws Exception {
|
||||
CLILogger.config(format("Rename movies using [%s]", service.getName()));
|
||||
|
||||
// handle movie files
|
||||
@ -910,4 +913,30 @@ public class CmdlineOperations implements CmdlineInterface {
|
||||
return format.format(new MediaBindingBean(file, file));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<File> extract(Collection<File> files, String output) throws Exception {
|
||||
// only keep single-volume archives or first part of multi-volume archives
|
||||
List<File> archiveFiles = filter(files, Archive.VOLUME_ONE_FILTER);
|
||||
List<File> extractedFiles = new ArrayList<File>();
|
||||
|
||||
for (File file : archiveFiles) {
|
||||
Archive archive = new Archive(file);
|
||||
File outputFolder = (output != null) ? new File(output).getAbsoluteFile() : new File(file.getParentFile(), getNameWithoutExtension(file.getName()));
|
||||
|
||||
CLILogger.info(String.format("Extract archive [%s] to [%s]", file.getName(), outputFolder));
|
||||
FileMapper outputMapper = new FileMapper(outputFolder, false);
|
||||
|
||||
List<File> entries = archive.listFiles();
|
||||
for (File entry : entries) {
|
||||
extractedFiles.add(outputMapper.getOutputFile(entry));
|
||||
}
|
||||
|
||||
CLILogger.finest("Extract files " + entries);
|
||||
archive.extract(outputMapper);
|
||||
}
|
||||
|
||||
return extractedFiles;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -61,11 +61,13 @@ import java.nio.ByteBuffer
|
||||
import java.nio.charset.Charset
|
||||
import static net.sourceforge.filebot.web.WebRequest.*
|
||||
|
||||
URL.metaClass.get = { readAll(getReader(delegate.openConnection())) }
|
||||
URL.metaClass.getText = { readAll(getReader(delegate.openConnection())) }
|
||||
URL.metaClass.getHtml = { new XmlParser(new org.cyberneko.html.parsers.SAXParser()).parseText(delegate.getText()) }
|
||||
URL.metaClass.fetch = { fetch(delegate) }
|
||||
URL.metaClass.getHtml = { new XmlParser(new org.cyberneko.html.parsers.SAXParser()).parseText(readAll(getReader(delegate.openConnection()))) }
|
||||
ByteBuffer.metaClass.getHtml = { csn = "utf-8" -> new XmlParser(new org.cyberneko.html.parsers.SAXParser()).parseText(Charset.forName(csn).decode(delegate.duplicate()).toString()) }
|
||||
ByteBuffer.metaClass.getText = { csn = "utf-8" -> Charset.forName(csn).decode(delegate.duplicate()).toString() }
|
||||
ByteBuffer.metaClass.getHtml = { csn = "utf-8" -> new XmlParser(new org.cyberneko.html.parsers.SAXParser()).parseText(delegate.getText(csn)) }
|
||||
|
||||
URL.metaClass.get = { delegate.getText() }
|
||||
URL.metaClass.post = { Map parameters -> post(delegate.openConnection(), parameters) }
|
||||
URL.metaClass.post = { byte[] data, contentType = 'application/octet-stream' -> post(delegate.openConnection(), data, contentType) }
|
||||
URL.metaClass.post = { String text, csn = 'utf-8' -> delegate.post(text.getBytes(csn), 'text/plain') }
|
||||
@ -212,6 +214,12 @@ def compute(args) { args = _defaults(args)
|
||||
}
|
||||
}
|
||||
|
||||
def extract(args) { args = _defaults(args)
|
||||
synchronized (_cli) {
|
||||
_guarded { _cli.extract(_files(args), args.output) }
|
||||
}
|
||||
}
|
||||
|
||||
def fetchEpisodeList(args) { args = _defaults(args)
|
||||
synchronized (_cli) {
|
||||
_guarded { _cli.fetchEpisodeList(args.query, args.format, args.db, args.order, args.lang) }
|
||||
|
@ -30,7 +30,10 @@
|
||||
<extension>zip</extension>
|
||||
</type>
|
||||
<type name="archive/rar">
|
||||
<extension>rar</extension>
|
||||
<extension>rar</extension>
|
||||
</type>
|
||||
<type name="archive/7z">
|
||||
<extension>7z</extension>
|
||||
</type>
|
||||
<type name="archive/gzip">
|
||||
<extension>gzip</extension>
|
||||
@ -39,6 +42,9 @@
|
||||
<type name="archive/tar">
|
||||
<extension>tar</extension>
|
||||
</type>
|
||||
<type name="archive/bzip2">
|
||||
<extension>bzip2</extension>
|
||||
</type>
|
||||
|
||||
<!-- Audio -->
|
||||
<type name="audio/mp3">
|
||||
@ -119,7 +125,7 @@
|
||||
<extension>sami</extension>
|
||||
</type>
|
||||
<type name="subtitle/VobSub">
|
||||
<extension>sub</extension>
|
||||
<extension>sub</extension>
|
||||
<extension>idx</extension>
|
||||
</type>
|
||||
</media-types>
|
@ -133,6 +133,10 @@
|
||||
</p>
|
||||
<h3 id="scripting">Scripting</h3>
|
||||
<p>
|
||||
<code><span class="cmd">filebot</span> <span class="option">-extract</span> <span class="argument">"300.part01.rar"</span> <span class="option">--output</span> <span class="argument">path/to/folder</span></code></code>
|
||||
Extract files from single-volume or multi-volume archives (e.g. rar).
|
||||
</p>
|
||||
<p>
|
||||
<code><span class="cmd">filebot</span> <span class="option">-list</span> <span class="option">--db</span> <span class="argument">thetvdb</span> <span class="option">--q</span> <span class="argument">Dexter</span> <span class="option">--format</span> <span class="argument">"{s}x{e.pad(2)} {t}"</span></code>
|
||||
Fetch episode list and print to console.
|
||||
</p>
|
||||
@ -223,6 +227,11 @@
|
||||
<td>create/check verification file</td>
|
||||
<td>folder or sfv file</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>-extract</td>
|
||||
<td>extract archives</td>
|
||||
<td>folder or archive file</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>--output</td>
|
||||
<td>output format and/or path</td>
|
||||
|
@ -129,13 +129,14 @@
|
||||
|
||||
<div class="documentation">
|
||||
<h4>Rename media files</h4>
|
||||
<pre><span class="return">File[]</span> <span class="method">rename</span>(<span class="property">folder</span>|<span class="property">file</span>, <span class="property">query</span>, <span class="property">format</span>, <span class="property">db</span>, <span class="property">lang</span>, <span class="property">strict</span>)</pre>
|
||||
<pre><span class="return">File[]</span> <span class="method">rename</span>(<span class="property">folder</span>|<span class="property">file</span>, <span class="property">query</span>, <span class="property">output</span>, <span class="property">format</span>, <span class="property">db</span>, <span class="property">lang</span>, <span class="property">strict</span>)</pre>
|
||||
<div class="text">Match files with episode/movie data and rename according to given naming scheme.</div>
|
||||
<dl>
|
||||
<dt>Parameters:</dt>
|
||||
<dd><span class="property">folder</span> - process media files in this folder</dd>
|
||||
<dd><span class="property">file</span> - process these media files</dd>
|
||||
<dd><span class="property">query</span> - force series/movie name, auto-detect if not set</dd>
|
||||
<dd><span class="property">output</span> - output folder, if format expression is not absolute</dd>
|
||||
<dd><span class="property">format</span> - episode/movie naming scheme</dd>
|
||||
<dd><span class="property">db</span> - episode/movie database</dd>
|
||||
<dd><span class="property">lang</span> - preferred language for episode/movie titles</dd>
|
||||
@ -186,6 +187,15 @@
|
||||
|
||||
<div class="documentation">
|
||||
<h4>Other</h4>
|
||||
<pre><span class="return">File[]</span> <span class="method">extract</span>(<span class="property">folder</span>|<span class="property">file</span>, <span class="property">output</span>)</pre>
|
||||
<div class="text">Extract files from single-volume or multi-volume archives (.zip, .rar, .7z, etc).</div>
|
||||
<dl>
|
||||
<dt>Parameters:</dt>
|
||||
<dd><span class="property">folder</span> - extract all archives that are in this folder</dd>
|
||||
<dd><span class="property">file</span> - extract this archive</dd>
|
||||
<dd><span class="property">output</span> - output folder, defaults to archive path</dd>
|
||||
</dl>
|
||||
<hr/>
|
||||
<pre><span class="return">String[]</span> <span class="method">fetchEpisodeList</span>(<span class="property">query</span>, <span class="property">format</span>, <span class="property">db</span>, <span class="property">lang</span>)</pre>
|
||||
<div class="text">Fetch episode data for the given tv show and format episode names.</div>
|
||||
<dl>
|
||||
|
Loading…
Reference in New Issue
Block a user