/* * This file is part of Popcorn Time. * * Popcorn Time is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Popcorn Time is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Popcorn Time. If not, see <http://www.gnu.org/licenses/>. */ package pct.droid.base.beaming.server; import android.content.Context; import android.net.wifi.WifiManager; import android.os.PowerManager; import com.koushikdutta.async.AsyncServer; import com.koushikdutta.async.http.Headers; import com.koushikdutta.async.http.HttpDate; import com.koushikdutta.async.http.server.AsyncHttpServer; import com.koushikdutta.async.http.server.AsyncHttpServerRequest; import com.koushikdutta.async.http.server.AsyncHttpServerResponse; import com.koushikdutta.async.http.server.HttpServerRequestCallback; import java.io.File; import java.util.Date; import java.util.HashMap; import pct.droid.base.PopcornApplication; import pct.droid.base.subs.FormatSRT; import pct.droid.base.subs.FormatVTT; import pct.droid.base.subs.TimedTextObject; import pct.droid.base.utils.FileUtils; import timber.log.Timber; public class BeamServer { public static final FileType MP4 = new FileType("mp4", "video/mp4", "DLNA.ORG_PN=AVC_MP4_BL_L3L_SD_AAC;DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000", "Streaming"), AVI = new FileType("avi", "video/x-msvideo", "DLNA.ORG_PN=AVC_MP4_BL_L3L_SD_AAC;DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000", "Streaming"), MKV = new FileType("mkv", "video/x-matroska", "DLNA.ORG_PN=AVC_MKV_MP_HD_AC3;DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01700000000000000000000000000000", "Streaming"), SRT = new FileType("srt", "application/x-subrip", "*", ""), VTT = new FileType("vtt", "text/vtt", "*", ""); private static FileType[] FILE_TYPES = {MP4, AVI, MKV}; private static FileType[] SUB_TYPES = {SRT, VTT}; private static HashMap<String, FileType> EXTENSIONS, CONTENT_TYPES; private static String sHost; private static Integer sPort; private static File sCurrentVideo = null, sCurrentSubs = null; private AsyncServer mAsyncServer = new AsyncServer(); private AsyncHttpServer mHttpServer; private PowerManager.WakeLock mWakeLock; private WifiManager.WifiLock mWifiLock; static { EXTENSIONS = new HashMap<>(); CONTENT_TYPES = new HashMap<>(); for (FileType localFileType : FILE_TYPES) { EXTENSIONS.put(localFileType.extension, localFileType); CONTENT_TYPES.put(localFileType.mimeType, localFileType); } CONTENT_TYPES.put("video/mpeg4", MP4); EXTENSIONS.put("3gp", MP4); EXTENSIONS.put("mov", MP4); } public BeamServer(String host, int port) { mHttpServer = new AsyncHttpServer() { protected boolean onRequest(AsyncHttpServerRequest httpServerRequest, AsyncHttpServerResponse httpServerResponse) { Timber.d(httpServerRequest.toString()); httpServerResponse.getHeaders().set("Access-Control-Allow-Origin", "*"); return super.onRequest(httpServerRequest, httpServerResponse); } }; sHost = host; sPort = port; for (FileType localFileType : FILE_TYPES) { VideoFileReponse localVideoFileReponse = new VideoFileReponse(localFileType); mHttpServer.get("/video." + localFileType.extension, localVideoFileReponse); mHttpServer.addAction("HEAD", "/video." + localFileType.extension, localVideoFileReponse); } for(FileType localSubsFileType : SUB_TYPES) { SubtitleFileResponse localSubsFileReponse = new SubtitleFileResponse(localSubsFileType); mHttpServer.get("/video." + localSubsFileType.extension, localSubsFileReponse); mHttpServer.addAction("HEAD", "/video." + localSubsFileType.extension, localSubsFileReponse); } mHttpServer.get("/(.*?)", new HttpServerRequestCallback() { @Override public void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) { response.send("Not found"); response.code(404); } }); } public static void setCurrentVideo(File file) { sCurrentVideo = file; } public static void setCurrentSubs(File file) { if(file == null) { sCurrentSubs = null; return; } sCurrentSubs = new File(file.getAbsolutePath().replace(".srt", "").replace(".vtt", "")); File vttFile = new File(sCurrentSubs.getAbsolutePath() + ".vtt"); File srtFile = new File(sCurrentSubs.getAbsolutePath() + ".srt"); try { if (FileUtils.getFileExtension(file.getName()).equals("srt") && !vttFile.exists() && srtFile.exists()) { FormatSRT srt = new FormatSRT(); TimedTextObject timedTextObject = srt.parseFile(file.getName(), FileUtils.getContentsAsString(file.getAbsolutePath())); String[] vttStr = timedTextObject.toVTT(); FileUtils.saveStringFile(vttStr, vttFile); } else if (FileUtils.getFileExtension(file.getName()).equals("vtt") && !srtFile.exists() && vttFile.exists()) { FormatVTT vtt = new FormatVTT(); TimedTextObject timedTextObject = vtt.parseFile(file.getName(), FileUtils.getContentsAsString(file.getAbsolutePath())); String[] srtStr = timedTextObject.toSRT(); FileUtils.saveStringFile(srtStr, srtFile); } } catch (Exception e) { e.printStackTrace(); } } public static void setCurrentVideo(String file) { setCurrentVideo(new File(file)); } public static void setCurrentSubs(String file) { setCurrentSubs(new File(file)); } public static void removeSubs() { sCurrentSubs = null; } public static String getHost() { return "http://" + sHost + ":" + sPort; } public static String getVideoURL() { return "http://" + sHost + ":" + sPort + "/video.mp4"; } public static String getSubsURL() { return "http://" + sHost + ":" + sPort + "/video." + SRT.extension; } public static String getSubsURL(FileType fileType) { return "http://" + sHost + ":" + sPort + "/video." + fileType.extension; } public void start() { mHttpServer.listen(mAsyncServer, sPort); PowerManager powerManager = (PowerManager) PopcornApplication.getAppContext().getSystemService(Context.POWER_SERVICE); mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "CastingServer"); mWakeLock.acquire(); WifiManager wifiManager = (WifiManager) PopcornApplication.getAppContext().getSystemService(Context.WIFI_SERVICE); mWifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "CastingServer"); mWifiLock.acquire(); } public void stop() { if(mHttpServer != null) mHttpServer.stop(); AsyncServer.getDefault().stop(); if(mAsyncServer != null) mAsyncServer.stop(); if(mWifiLock != null && mWifiLock.isHeld()) mWifiLock.release(); if(mWakeLock != null && mWakeLock.isHeld()) mWakeLock.release(); } public static class FileType { public final String dlnaContentFeatures; public final String dlnaTransferMode; public final String extension; public final String mimeType; public FileType(String extension, String mimeType, String dlnaContentFeatures, String dlnaTransferMode) { this.extension = extension; this.mimeType = mimeType; this.dlnaContentFeatures = dlnaContentFeatures; this.dlnaTransferMode = dlnaTransferMode; } public final boolean isImage() { return this.mimeType.startsWith("image/"); } public boolean isVideo() { return this.mimeType.startsWith("video/"); } public void setHeaders(Headers paramHeaders) { paramHeaders.set("contentFeatures.dlna.org", this.dlnaContentFeatures); paramHeaders.set("TransferMode.DLNA.ORG", this.dlnaTransferMode); paramHeaders.set("DAAP-Server", "iTunes/11.0.5 (OS X)"); paramHeaders.set("Date", HttpDate.format(new Date())); paramHeaders.set("Last-Modified", "2015-01-01T10:00:00Z"); paramHeaders.set("Content-Type", this.mimeType); paramHeaders.set("CaptionInfo.sec", getSubsURL(SRT)); } public void setHeaders(AsyncHttpServerResponse httpServerResponse) { setHeaders(httpServerResponse.getHeaders()); } } class VideoFileReponse implements HttpServerRequestCallback { FileType mFileType; public VideoFileReponse(FileType fileType) { mFileType = fileType; } public void onRequest(AsyncHttpServerRequest asyncHttpServerRequest, AsyncHttpServerResponse httpServerResponse) { if (sCurrentVideo != null && sCurrentVideo.exists()) { mFileType.setHeaders(httpServerResponse); httpServerResponse.sendFile(sCurrentVideo); } else { httpServerResponse.send("Not found"); httpServerResponse.code(404); } Timber.i(httpServerResponse.toString()); } } class SubtitleFileResponse implements HttpServerRequestCallback { FileType mFileType; public SubtitleFileResponse(FileType fileType) { mFileType = fileType; } public void onRequest(AsyncHttpServerRequest asyncHttpServerRequest, AsyncHttpServerResponse httpServerResponse) { File file = new File(sCurrentSubs.getAbsolutePath() + "." + mFileType.extension); if (sCurrentSubs != null && file.exists()) { mFileType.setHeaders(httpServerResponse); httpServerResponse.sendFile(file); } else { httpServerResponse.send("Not found"); httpServerResponse.code(404); } Timber.i(httpServerResponse.toString()); } } }