diff --git a/Homarus/README.md b/Homarus/README.md
new file mode 100644
index 00000000..07ee4340
--- /dev/null
+++ b/Homarus/README.md
@@ -0,0 +1,58 @@
+# Homarus
+
+## Introduction
+
+[FFmpeg](https://www.ffmpeg.org/) as a microservice.
+
+## Installation
+- Install `ffmpeg`. On Ubuntu, this can be done with `sudo apt-get install ffmpeg`.
+- Clone this repository somewhere in your web root (example: `/var/www/html/Crayfish/Homarus`).
+- Copy `/var/www/html/Crayfish/Homarus/cfg/config.default.yml` to `/var/www/html/Crayfish/Homarus/cfg/config.yml`
+- Copy `/var/www/html/Crayfish/Hypercube/syn-settings.xml` to `/var/www/html/Crayfish/Homarus/syn-settings.xml`
+- Install `composer`. [Install instructions here.][4]
+- `$ cd /path/to/Homarus` and run `$ composer install`
+- Then either
+ - For production, configure your web server appropriately (e.g. add a VirtualHost for Homarus in Apache) OR
+ - For development, run the PHP built-in webserver `$ php -S localhost:8888 -t src` from Homarus root.
+
+
+### Apache2
+
+To use Homarus with Apache you need to configure your Virtualhost with a few options:
+- Redirect all requests to the Homarus index.php file
+- Make sure Hypercube has access to Authorization headers
+
+Here is an example configuration for Apache 2.4:
+```apache
+Alias "/homarus" "/var/www/html/Crayfish/Homarus/src"
+
+ FallbackResource /homarus/index.php
+ Require all granted
+ DirectoryIndex index.php
+ SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
+
+```
+
+This will put the Homarus at the /homarus endpoint on the webserver.
+
+## Configuration
+
+If your ffmpeg installation is not on your path, then you can configure homarus to use a specific executable by editing `executable` entry in [config.yaml](./cfg/config.example.yaml).
+
+You also will need to set the `fedora base url` entry to point to your Fedora installation.
+
+## Usage
+This will return the an avi file for the test video file in Fedora.
+```
+curl -H "Authorization: Bearer islandora" -H "Accept: video/x-msvideo" -H "Apix-Ldp-Resource:http://localhost:8080/fcrepo/rest/testvideo" http://localhost:8000/homarus/convert --output output.avi
+```
+
+## Maintainers
+
+Current maintainers:
+
+* [Natkeeran](https://github.com/Natkeeran)
+
+## License
+
+[MIT](https://opensource.org/licenses/MIT)
diff --git a/Homarus/cfg/config.example.yaml b/Homarus/cfg/config.example.yaml
new file mode 100644
index 00000000..f3b36314
--- /dev/null
+++ b/Homarus/cfg/config.example.yaml
@@ -0,0 +1,37 @@
+---
+homarus:
+ # path to the ffmpeg executable
+ executable: ffmpeg
+ mime_types:
+ valid:
+ - video/mp4
+ - video/x-msvideo
+ - video/ogg
+ default_video: video/mp4
+ mime_to_format:
+ valid:
+ - video/mp4_mp4
+ - video/x-msvideo_avi
+ - video/ogg_ogg
+
+fedora_resource:
+ base_url: http://localhost:8080/fcrepo/rest
+
+log:
+ # Valid log levels are:
+ # DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL, ALERT, EMERGENCY, NONE
+ # log level none won't open logfile
+ level: DEBUG
+ file: /var/log/islandora/homarus.log
+
+syn:
+ # toggles JWT security for service
+ enable: True
+ # Path to the syn config file for authentication.
+ # example can be found here:
+ # https://github.com/Islandora-CLAW/Syn/blob/master/conf/syn-settings.example$
+ config: ../syn-settings.xml
+
+
+
+
diff --git a/Homarus/composer.json b/Homarus/composer.json
new file mode 100644
index 00000000..eaac1de2
--- /dev/null
+++ b/Homarus/composer.json
@@ -0,0 +1,43 @@
+{
+ "name": "islandora/homarus",
+ "description": "FFmpeg as a web service",
+ "type": "project",
+ "require": {
+ "islandora/crayfish-commons": "dev-master"
+ },
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Islandora Foundation",
+ "email": "community@islandora.ca",
+ "role": "Owner"
+ },
+ {
+ "name": "Natkeeran L.Kanthan",
+ "email": "nat.ledchumykanthan@utoronto.ca",
+ "role": "Maintainer"
+ }
+ ],
+ "autoload": {
+ "psr-4": {
+ "Islandora\\Homarus\\": "src/"
+ }
+ },
+ "scripts": {
+ "check": [
+ "phpcs --standard=PSR2 src tests",
+ "phpcpd --names *.php src"
+ ],
+ "test": [
+ "@check",
+ "phpunit"
+ ]
+ },
+ "require-dev": {
+ "symfony/browser-kit": "^3.0",
+ "symfony/css-selector": "^3.0",
+ "phpunit/phpunit": "^5.0",
+ "squizlabs/php_codesniffer": "^2.0",
+ "sebastian/phpcpd": "^3.0"
+ }
+}
diff --git a/Homarus/phpunit.xml.dist b/Homarus/phpunit.xml.dist
new file mode 100644
index 00000000..c14c1607
--- /dev/null
+++ b/Homarus/phpunit.xml.dist
@@ -0,0 +1,30 @@
+
+
+
+
+ ./tests/
+
+
+
+
+
+
+
+
+ ./src/index.php
+ ./src/app.php
+
+ ./src
+
+
+
diff --git a/Homarus/src/Controller/HomarusController.php b/Homarus/src/Controller/HomarusController.php
new file mode 100644
index 00000000..bda261b4
--- /dev/null
+++ b/Homarus/src/Controller/HomarusController.php
@@ -0,0 +1,144 @@
+cmd = $cmd;
+ $this->formats = $formats;
+ $this->default_format = $default_format;
+ $this->executable = $executable;
+ $this->log = $log;
+ $this->mime_to_format = $mime_to_format;
+ }
+
+ /**
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ * @return \Symfony\Component\HttpFoundation\Response|\Symfony\Component\HttpFoundation\StreamedResponse
+ */
+ public function convert(Request $request) {
+ $this->log->info('Ffmpeg Convert request.');
+
+ // Short circuit if there's no Apix-Ldp-Resource header.
+ if (!$request->headers->has("Apix-Ldp-Resource"))
+ {
+ $this->log->debug("Malformed request, no Apix-Ldp-Resource header present");
+ return new Response(
+ "Malformed request, no Apix-Ldp-Resource header present",
+ 400
+ );
+ } else {
+ $source = $request->headers->get('Apix-Ldp-Resource');
+ }
+
+ // Find the format
+ $content_types = $request->getAcceptableContentTypes();
+ $content_type = $this->get_content_type($content_types);
+ $format = $this->get_ffmpeg_format($content_type);
+
+ $cmd_params = "";
+ if($format == "mp4") {
+ $cmd_params = " -vcodec libx264 -preset medium -acodec aac -strict -2 -ab 128k -ac 2 -async 1 -movflags frag_keyframe+empty_moov ";
+ }
+
+ // Arguments to ffmpeg command are sent as a custom header
+ $args = $request->headers->get('X-Islandora-Args');
+ $this->log->debug("X-Islandora-Args:", ['args' => $args]);
+
+ $cmd_string = "$this->executable -i $source $cmd_params -f $format -";
+ $this->log->info('Ffempg Command:', ['cmd' => $cmd_string]);
+
+ // Return response.
+ try {
+ return new StreamedResponse(
+ $this->cmd->execute($cmd_string, $source),
+ 200,
+ ['Content-Type' => $content_type]
+ );
+ } catch (\RuntimeException $e) {
+ $this->log->error("RuntimeException:", ['exception' => $e]);
+ return new Response($e->getMessage(), 500);
+ }
+ }
+
+
+ private function get_content_type($content_types) {
+ $content_type = null;
+ foreach ($content_types as $type) {
+ if (in_array($type, $this->formats)) {
+ $content_type = $type;
+ break;
+ }
+ }
+
+ if ($content_type === null) {
+ $content_type = $this->default_format;
+ $this->log->info('Falling back to default content type');
+ }
+ return $content_type;
+ }
+
+ private function get_ffmpeg_format($content_type){
+ foreach ($this->mime_to_format as $format) {
+ if (strpos($format, $content_type) !== false) {
+ $this->log->info("does it get here");
+ $format_info = explode("_", $format);
+ break;
+ }
+ }
+ return $format_info[1];
+ }
+
+ /**
+ * @return \Symfony\Component\HttpFoundation\BinaryFileResponse
+ */
+ public function convertOptions()
+ {
+ return new BinaryFileResponse(
+ __DIR__ . "/../../static/convert.ttl",
+ 200,
+ ['Content-Type' => 'text/turtle']
+ );
+ }
+
+}
diff --git a/Homarus/src/app.php b/Homarus/src/app.php
new file mode 100644
index 00000000..dca69e57
--- /dev/null
+++ b/Homarus/src/app.php
@@ -0,0 +1,30 @@
+register(new IslandoraServiceProvider());
+$app->register(new YamlConfigServiceProvider(__DIR__ . '/../cfg/config.yaml'));
+
+$app['homarus.controller'] = function ($app) {
+ return new HomarusController(
+ $app['crayfish.cmd_execute_service'],
+ $app['crayfish.homarus.mime_types.valid'],
+ $app['crayfish.homarus.mime_types.default_video'],
+ $app['crayfish.homarus.executable'],
+ $app['monolog'],
+ $app['crayfish.homarus.mime_to_format']
+ );
+};
+
+$app->options('/convert', "homarus.controller:convertOptions");
+$app->get('/convert', "homarus.controller:convert");
+
+return $app;
diff --git a/Homarus/src/index.php b/Homarus/src/index.php
new file mode 100644
index 00000000..3e23d477
--- /dev/null
+++ b/Homarus/src/index.php
@@ -0,0 +1,4 @@
+run();
diff --git a/Homarus/static/convert.ttl b/Homarus/static/convert.ttl
new file mode 100644
index 00000000..ccda85c7
--- /dev/null
+++ b/Homarus/static/convert.ttl
@@ -0,0 +1,18 @@
+@prefix apix: .
+@prefix owl: .
+@prefix ebucore: .
+@prefix ldp: .
+@prefix islandora: .
+@prefix rdfs: .
+
+<> a apix:Extension;
+ rdfs:label "Ffmpeg Service";
+ rdfs:comment "ffmpeg as a microservice";
+ apix:exposesService islandora:ConvertService;
+ apix:exposesServiceAt "svc:convert";
+ apix:bindsTo <#class> .
+
+<#class> owl:intersectionOf (
+ ldp:NonRDFSource
+ [ a owl:Restriction; owl:onProperty ebucore:hasMimeType; owl:hasValue "video/mp4", "video/x-msvideo", "video/ogg" ]
+) .
\ No newline at end of file
diff --git a/Homarus/tests/Islandora/Homarus/Tests/HomarusControllerTest.php b/Homarus/tests/Islandora/Homarus/Tests/HomarusControllerTest.php
new file mode 100644
index 00000000..0a3c319f
--- /dev/null
+++ b/Homarus/tests/Islandora/Homarus/Tests/HomarusControllerTest.php
@@ -0,0 +1,44 @@
+prophesize(CmdExecuteService::class)->reveal();
+ $controller = new HomarusController(
+ $mock_service,
+ [],
+ '',
+ 'convert',
+ $this->prophesize(Logger::class)->reveal(),
+ ''
+ );
+
+ $response = $controller->convertOptions();
+ $this->assertTrue($response->getStatusCode() == 200, 'Convert OPTIONS should return 200');
+ $this->assertTrue(
+ $response->headers->get('Content-Type') == 'text/turtle',
+ 'Convert OPTIONS should return turtle'
+ );
+ }
+
+}