From b44056a2559321cb6620fe016bab081e4038e977 Mon Sep 17 00:00:00 2001 From: hensm Date: Wed, 1 May 2019 23:20:39 +0100 Subject: [PATCH] Move http media server to separate class --- .../AppDelegate.swift | 4 + app/src/MediaServer.ts | 73 +++++++++++++++++++ app/src/main.ts | 63 +++++----------- app/src/types.ts | 2 +- ext/src/_locales/en/messages.json | 8 +- ext/src/main.ts | 2 - ext/src/mediaCast.js | 6 +- 7 files changed, 104 insertions(+), 54 deletions(-) create mode 100644 app/src/MediaServer.ts diff --git a/app/NativeMacReceiverSelector/AppDelegate.swift b/app/NativeMacReceiverSelector/AppDelegate.swift index 939eca7..d704d9e 100644 --- a/app/NativeMacReceiverSelector/AppDelegate.swift +++ b/app/NativeMacReceiverSelector/AppDelegate.swift @@ -39,6 +39,10 @@ extension AppDelegate: NSApplicationDelegate { NSApp.activate(ignoringOtherApps: true) } + func applicationDidResignActive(_ aNotification: Notification) { + self.mainWindow?.performClose(aNotification) + } + func applicationWillTerminate(_ aNotification: Notification) {} func applicationShouldTerminateAfterLastWindowClosed(_ app: NSApplication) -> Bool { diff --git a/app/src/MediaServer.ts b/app/src/MediaServer.ts new file mode 100644 index 0000000..702d009 --- /dev/null +++ b/app/src/MediaServer.ts @@ -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); + } + } +} diff --git a/app/src/main.ts b/app/src/main.ts index dae16a8..f966a91 100755 --- a/app/src/main.ts +++ b/app/src/main.ts @@ -8,6 +8,7 @@ import mime from "mime-types"; import path from "path"; import Media from "./Media"; +import MediaServer from "./MediaServer"; import Session from "./Session"; import StatusListener from "./StatusListener"; import * as transforms from "./transforms"; @@ -27,11 +28,11 @@ events.EventEmitter.defaultMaxListeners = 50; const browser = new dnssd.Browser(dnssd.tcp("googlecast")); // Local media server -let httpServer: http.Server; +let mediaServer: MediaServer; process.on("SIGTERM", () => { - if (httpServer) { - httpServer.close(); + if (mediaServer) { + mediaServer.stop(); } }); @@ -182,58 +183,32 @@ async function handleMessage (message: Message) { } - case "bridge:/startHttpServer": { + case "bridge:/mediaServer/start": { const { filePath, port } = message.data; - httpServer = http.createServer((req, res) => { - const { size: fileSize } = fs.statSync(filePath); - const { range } = req.headers; + mediaServer = new MediaServer(filePath, port); + mediaServer.start(); - const contentType = mime.lookup(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(filePath, { start, end }).pipe(res); - - } else { - res.writeHead(200, { - "Content-Length": fileSize - , "Content-Type": contentType - }); - - fs.createReadStream(filePath).pipe(res); - } - }); - - httpServer.listen(port, () => { + mediaServer.on("started", () => { sendMessage({ - subject: "mediaCast:/httpServerStarted" + subject: "mediaCast:/mediaServer/started" }); }); + mediaServer.on("stopped", () => { + sendMessage({ + subject: "mediaCast:/mediaServer/stopped" + }); + }) + break; } - case "bridge:/stopHttpServer": { - if (httpServer) { - httpServer.close(); + case "bridge:/mediaServer/stop": { + if (mediaServer) { + mediaServer.stop(); } + break; } } diff --git a/app/src/types.ts b/app/src/types.ts index 4759a36..2513d99 100644 --- a/app/src/types.ts +++ b/app/src/types.ts @@ -2,7 +2,7 @@ export interface Message { subject: string; - data: any; + data?: any; _id?: string; } diff --git a/ext/src/_locales/en/messages.json b/ext/src/_locales/en/messages.json index 5423207..ecb98c5 100755 --- a/ext/src/_locales/en/messages.json +++ b/ext/src/_locales/en/messages.json @@ -119,7 +119,7 @@ "message": "HTML5 video/audio media casting." } , "optionsMediaEnabled": { - "message": "Media casting enabled" + "message": "Enable media casting" } , "optionsMediaSyncElement": { "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." } , "optionsLocalMediaEnabled": { - "message": "Local media casting enabled" + "message": "Enable local media casting" } , "optionsLocalMediaServerPort": { "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." } , "optionsUserAgentWhitelistEnabled": { - "message": "Site whitelist enabled" + "message": "Enable site whitelist" } , "optionsUserAgentWhitelistContent": { "message": "Match patterns (newline-separated):" @@ -182,7 +182,7 @@ "message": "Screen/Tab mirroring to a Chromecast receiver app." } , "optionsMirroringEnabled": { - "message": "Screen mirroring enabled" + "message": "Enable screen mirroring" } , "optionsMirroringAppId": { "message": "Receiver app ID:" diff --git a/ext/src/main.ts b/ext/src/main.ts index a4f954d..0879aac 100755 --- a/ext/src/main.ts +++ b/ext/src/main.ts @@ -373,8 +373,6 @@ const statusBridge = browser.runtime.connectNative(APPLICATION_NAME); const statusBridgeReceivers = new Map(); statusBridge.onMessage.addListener(async (message: Message) => { - console.log(message); - switch (message.subject) { case "shim:/serviceUp": { const receiver = (message as ServiceUpMessage).data; diff --git a/ext/src/mediaCast.js b/ext/src/mediaCast.js index ca87b37..562a18b 100644 --- a/ext/src/mediaCast.js +++ b/ext/src/mediaCast.js @@ -18,7 +18,7 @@ const mediaElement = browser.menus.getTargetElement(targetElementId); window.addEventListener("beforeunload", () => { browser.runtime.sendMessage({ - subject: "bridge:/stopHttpServer" + subject: "bridge:/mediaServer/stop" }); if (options.mediaStopOnUnload) { @@ -58,7 +58,7 @@ async function onRequestSessionSuccess (session_) { if (isLocalFile) { await new Promise((resolve, reject) => { browser.runtime.sendMessage({ - subject: "bridge:/startHttpServer" + subject: "bridge:/mediaServer/start" , data: { filePath: decodeURI(mediaUrl.pathname) , port @@ -66,7 +66,7 @@ async function onRequestSessionSuccess (session_) { }); browser.runtime.onMessage.addListener(function onMessage (message) { - if (message.subject === "mediaCast:/httpServerStarted") { + if (message.subject === "mediaCast:/mediaServer/started") { browser.runtime.onMessage.removeListener(onMessage); resolve(); }