Move http media server to separate class

This commit is contained in:
hensm
2019-05-01 23:20:39 +01:00
committed by Matt Hensman
parent fc1dd28254
commit b44056a255
7 changed files with 104 additions and 54 deletions

View File

@@ -39,6 +39,10 @@ extension AppDelegate: NSApplicationDelegate {
NSApp.activate(ignoringOtherApps: true) NSApp.activate(ignoringOtherApps: true)
} }
func applicationDidResignActive(_ aNotification: Notification) {
self.mainWindow?.performClose(aNotification)
}
func applicationWillTerminate(_ aNotification: Notification) {} func applicationWillTerminate(_ aNotification: Notification) {}
func applicationShouldTerminateAfterLastWindowClosed(_ app: NSApplication) -> Bool { func applicationShouldTerminateAfterLastWindowClosed(_ app: NSApplication) -> Bool {

73
app/src/MediaServer.ts Normal file
View File

@@ -0,0 +1,73 @@
"use strict";
import fs from "fs";
import http from "http";
import mime from "mime-types";
import EventEmitter from "events";
import { Message
, SendMessageCallback } from "./types";
export default class MediaServer extends EventEmitter {
private httpServer: http.Server;
constructor (
private filePath: string
, private port: number) {
super();
this.httpServer = http.createServer(this.requestListener.bind(this));
}
public start () {
this.httpServer.listen(this.port, () => {
this.emit("started");
});
}
public stop () {
if (this.httpServer && this.httpServer.listening) {
this.httpServer.close(() => {
this.emit("stopped");
});
}
}
private requestListener (
req: http.IncomingMessage
, res: http.ServerResponse) {
const { size: fileSize } = fs.statSync(this.filePath);
const { range } = req.headers;
const contentType = mime.lookup(this.filePath) || "video/mp4";
// Partial content HTTP 206
if (range) {
const bounds = range.substring(6).split("-");
const start = parseInt(bounds[0]);
const end = bounds[1] ? parseInt(bounds[1]) : fileSize - 1;
const chunkSize = (end - start) + 1;
res.writeHead(206, {
"Accept-Ranges": "bytes"
, "Content-Range": `bytes ${start}-${end}/${fileSize}`
, "Content-Length": chunkSize
, "Content-Type": contentType
});
fs.createReadStream(this.filePath, { start, end }).pipe(res);
} else {
res.writeHead(200, {
"Content-Length": fileSize
, "Content-Type": contentType
});
fs.createReadStream(this.filePath).pipe(res);
}
}
}

View File

@@ -8,6 +8,7 @@ import mime from "mime-types";
import path from "path"; import path from "path";
import Media from "./Media"; import Media from "./Media";
import MediaServer from "./MediaServer";
import Session from "./Session"; import Session from "./Session";
import StatusListener from "./StatusListener"; import StatusListener from "./StatusListener";
import * as transforms from "./transforms"; import * as transforms from "./transforms";
@@ -27,11 +28,11 @@ events.EventEmitter.defaultMaxListeners = 50;
const browser = new dnssd.Browser(dnssd.tcp("googlecast")); const browser = new dnssd.Browser(dnssd.tcp("googlecast"));
// Local media server // Local media server
let httpServer: http.Server; let mediaServer: MediaServer;
process.on("SIGTERM", () => { process.on("SIGTERM", () => {
if (httpServer) { if (mediaServer) {
httpServer.close(); mediaServer.stop();
} }
}); });
@@ -182,58 +183,32 @@ async function handleMessage (message: Message) {
} }
case "bridge:/startHttpServer": { case "bridge:/mediaServer/start": {
const { filePath, port } = message.data; const { filePath, port } = message.data;
httpServer = http.createServer((req, res) => { mediaServer = new MediaServer(filePath, port);
const { size: fileSize } = fs.statSync(filePath); mediaServer.start();
const { range } = req.headers;
const contentType = mime.lookup(filePath) || "video/mp4"; mediaServer.on("started", () => {
// Partial content HTTP 206
if (range) {
const bounds = range.substring(6).split("-");
const start = parseInt(bounds[0]);
const end = bounds[1]
? parseInt(bounds[1])
: fileSize - 1;
const chunkSize = (end - start) + 1;
res.writeHead(206, {
"Accept-Ranges": "bytes"
, "Content-Range": `bytes ${start}-${end}/${fileSize}`
, "Content-Length": chunkSize
, "Content-Type": contentType
});
fs.createReadStream(filePath, { start, end }).pipe(res);
} else {
res.writeHead(200, {
"Content-Length": fileSize
, "Content-Type": contentType
});
fs.createReadStream(filePath).pipe(res);
}
});
httpServer.listen(port, () => {
sendMessage({ sendMessage({
subject: "mediaCast:/httpServerStarted" subject: "mediaCast:/mediaServer/started"
}); });
}); });
mediaServer.on("stopped", () => {
sendMessage({
subject: "mediaCast:/mediaServer/stopped"
});
})
break; break;
} }
case "bridge:/stopHttpServer": { case "bridge:/mediaServer/stop": {
if (httpServer) { if (mediaServer) {
httpServer.close(); mediaServer.stop();
} }
break; break;
} }
} }

View File

@@ -2,7 +2,7 @@
export interface Message { export interface Message {
subject: string; subject: string;
data: any; data?: any;
_id?: string; _id?: string;
} }

View File

@@ -119,7 +119,7 @@
"message": "HTML5 video/audio media casting." "message": "HTML5 video/audio media casting."
} }
, "optionsMediaEnabled": { , "optionsMediaEnabled": {
"message": "Media casting enabled" "message": "Enable media casting"
} }
, "optionsMediaSyncElement": { , "optionsMediaSyncElement": {
"message": "Sync receiver state with media element" "message": "Sync receiver state with media element"
@@ -135,7 +135,7 @@
"message": "HTTP server started by the bridge app to stream local media files to the cast receiver." "message": "HTTP server started by the bridge app to stream local media files to the cast receiver."
} }
, "optionsLocalMediaEnabled": { , "optionsLocalMediaEnabled": {
"message": "Local media casting enabled" "message": "Enable local media casting"
} }
, "optionsLocalMediaServerPort": { , "optionsLocalMediaServerPort": {
"message": "HTTP server port:" "message": "HTTP server port:"
@@ -148,7 +148,7 @@
"message": "Sites for which to replace the user agent with a Chrome version for compatibility. Must be valid match patterns." "message": "Sites for which to replace the user agent with a Chrome version for compatibility. Must be valid match patterns."
} }
, "optionsUserAgentWhitelistEnabled": { , "optionsUserAgentWhitelistEnabled": {
"message": "Site whitelist enabled" "message": "Enable site whitelist"
} }
, "optionsUserAgentWhitelistContent": { , "optionsUserAgentWhitelistContent": {
"message": "Match patterns (newline-separated):" "message": "Match patterns (newline-separated):"
@@ -182,7 +182,7 @@
"message": "Screen/Tab mirroring to a Chromecast receiver app." "message": "Screen/Tab mirroring to a Chromecast receiver app."
} }
, "optionsMirroringEnabled": { , "optionsMirroringEnabled": {
"message": "Screen mirroring enabled" "message": "Enable screen mirroring"
} }
, "optionsMirroringAppId": { , "optionsMirroringAppId": {
"message": "Receiver app ID:" "message": "Receiver app ID:"

View File

@@ -373,8 +373,6 @@ const statusBridge = browser.runtime.connectNative(APPLICATION_NAME);
const statusBridgeReceivers = new Map<string, Receiver>(); const statusBridgeReceivers = new Map<string, Receiver>();
statusBridge.onMessage.addListener(async (message: Message) => { statusBridge.onMessage.addListener(async (message: Message) => {
console.log(message);
switch (message.subject) { switch (message.subject) {
case "shim:/serviceUp": { case "shim:/serviceUp": {
const receiver = (message as ServiceUpMessage).data; const receiver = (message as ServiceUpMessage).data;

View File

@@ -18,7 +18,7 @@ const mediaElement = browser.menus.getTargetElement(targetElementId);
window.addEventListener("beforeunload", () => { window.addEventListener("beforeunload", () => {
browser.runtime.sendMessage({ browser.runtime.sendMessage({
subject: "bridge:/stopHttpServer" subject: "bridge:/mediaServer/stop"
}); });
if (options.mediaStopOnUnload) { if (options.mediaStopOnUnload) {
@@ -58,7 +58,7 @@ async function onRequestSessionSuccess (session_) {
if (isLocalFile) { if (isLocalFile) {
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
browser.runtime.sendMessage({ browser.runtime.sendMessage({
subject: "bridge:/startHttpServer" subject: "bridge:/mediaServer/start"
, data: { , data: {
filePath: decodeURI(mediaUrl.pathname) filePath: decodeURI(mediaUrl.pathname)
, port , port
@@ -66,7 +66,7 @@ async function onRequestSessionSuccess (session_) {
}); });
browser.runtime.onMessage.addListener(function onMessage (message) { browser.runtime.onMessage.addListener(function onMessage (message) {
if (message.subject === "mediaCast:/httpServerStarted") { if (message.subject === "mediaCast:/mediaServer/started") {
browser.runtime.onMessage.removeListener(onMessage); browser.runtime.onMessage.removeListener(onMessage);
resolve(); resolve();
} }