├── .gitattributes
├── server
├── public
├── views
│ ├── index.jade
│ └── layout.jade
├── routes
│ ├── index.js
│ ├── camera.js
│ └── sony-camera.js
└── app.js
├── client
├── app
│ ├── .buildignore
│ ├── camera
│ │ └── photos
│ │ │ └── .keep
│ ├── styles
│ │ ├── main.css
│ │ └── main.scss
│ ├── robots.txt
│ ├── favicon.ico
│ ├── images
│ │ └── yeoman.png
│ ├── scripts
│ │ ├── app.js
│ │ └── controllers
│ │ │ └── main.js
│ ├── views
│ │ └── main.html
│ ├── index.html
│ ├── 404.html
│ └── .htaccess
├── test
│ ├── runner.html
│ ├── spec
│ │ └── controllers
│ │ │ └── main.js
│ └── .jshintrc
└── karma-e2e.conf.js
├── .bowerrc
├── .gitignore
├── .travis.yml
├── .jshintrc
├── .editorconfig
├── bower.json
├── LICENSE
├── karma.conf.js
├── package.json
├── README.md
└── Gruntfile.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/server/public:
--------------------------------------------------------------------------------
1 | ../client/app
--------------------------------------------------------------------------------
/client/app/.buildignore:
--------------------------------------------------------------------------------
1 | *.coffee
--------------------------------------------------------------------------------
/client/app/camera/photos/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/app/styles/main.css:
--------------------------------------------------------------------------------
1 | ../../dist/styles/main.css
--------------------------------------------------------------------------------
/client/app/robots.txt:
--------------------------------------------------------------------------------
1 | # robotstxt.org
2 |
3 | User-agent: *
4 |
--------------------------------------------------------------------------------
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "client/app/bower_components"
3 | }
4 |
--------------------------------------------------------------------------------
/client/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eqot/RemoteCamera/HEAD/client/app/favicon.ico
--------------------------------------------------------------------------------
/server/views/index.jade:
--------------------------------------------------------------------------------
1 | extends layout
2 |
3 | block content
4 | h1= title
5 | p Welcome to #{title}
--------------------------------------------------------------------------------
/client/app/images/yeoman.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eqot/RemoteCamera/HEAD/client/app/images/yeoman.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | client/dist
3 | .tmp
4 | .sass-cache
5 | client/app/bower_components
6 | client/app/camera/photos/*.JPG
7 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '0.10'
4 | before_script:
5 | - 'npm install -g bower grunt-cli'
6 | - 'bower install'
7 |
--------------------------------------------------------------------------------
/server/views/layout.jade:
--------------------------------------------------------------------------------
1 | doctype 5
2 | html
3 | head
4 | title= title
5 | link(rel='stylesheet', href='/stylesheets/style.css')
6 | body
7 | block content
--------------------------------------------------------------------------------
/server/routes/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var publicPath = require('path').join(__dirname, '../public/');
4 |
5 | /*
6 | * GET home page.
7 | */
8 |
9 | exports.index = function(req, res){
10 | res.sendfile(publicPath + 'index.html');
11 | };
12 |
--------------------------------------------------------------------------------
/client/test/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | End2end Test Runner
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/client/app/styles/main.scss:
--------------------------------------------------------------------------------
1 | $icon-font-path: "/bower_components/sass-bootstrap/fonts/";
2 |
3 | @import 'sass-bootstrap/lib/bootstrap';
4 |
5 | /* Space out content a bit */
6 | body {
7 | padding-top: 20px;
8 | padding-bottom: 20px;
9 | background-color: black;
10 | }
11 |
12 | #liveview {
13 | position: absolute;
14 | top: 0;
15 | z-index: -1;
16 | }
17 |
--------------------------------------------------------------------------------
/client/app/scripts/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('clientApp', [
4 | 'ngCookies',
5 | 'ngResource',
6 | 'ngSanitize',
7 | 'ngRoute'
8 | ])
9 | .config(function ($routeProvider) {
10 | $routeProvider
11 | .when('/', {
12 | templateUrl: 'views/main.html',
13 | controller: 'MainCtrl'
14 | })
15 | .otherwise({
16 | redirectTo: '/'
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "esnext": true,
5 | "bitwise": true,
6 | "camelcase": true,
7 | "curly": true,
8 | "eqeqeq": true,
9 | "immed": true,
10 | "indent": 2,
11 | "latedef": true,
12 | "newcap": true,
13 | "noarg": true,
14 | "quotmark": "single",
15 | "regexp": true,
16 | "undef": true,
17 | "unused": true,
18 | "strict": true,
19 | "trailing": true,
20 | "smarttabs": true,
21 | "globals": {
22 | "angular": false
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 |
10 | # Change these settings to your own preference
11 | indent_style = space
12 | indent_size = 2
13 |
14 | # We recommend you to keep these unchanged
15 | end_of_line = lf
16 | charset = utf-8
17 | trim_trailing_whitespace = true
18 | insert_final_newline = true
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "RemoteCamera",
3 | "version": "0.0.0",
4 | "appPath": "client/app",
5 | "distPath": "client/dist",
6 | "dependencies": {
7 | "angular": "~1.2.0",
8 | "json3": "~3.2.4",
9 | "es5-shim": "~2.1.0",
10 | "jquery": "~1.10.2",
11 | "sass-bootstrap": "~3.0.0",
12 | "angular-resource": "~1.2.0",
13 | "angular-cookies": "~1.2.0",
14 | "angular-sanitize": "~1.2.0",
15 | "angular-route": "~1.2.0"
16 | },
17 | "devDependencies": {
18 | "angular-mocks": "~1.2.0",
19 | "angular-scenario": "~1.2.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/client/test/spec/controllers/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Controller: MainCtrl', function () {
4 |
5 | // load the controller's module
6 | beforeEach(module('clientApp'));
7 |
8 | var MainCtrl,
9 | scope;
10 |
11 | // Initialize the controller and a mock scope
12 | beforeEach(inject(function ($controller, $rootScope) {
13 | scope = $rootScope.$new();
14 | MainCtrl = $controller('MainCtrl', {
15 | $scope: scope
16 | });
17 | }));
18 |
19 | it('should attach a list of awesomeThings to the scope', function () {
20 | expect(scope.awesomeThings.length).toBe(3);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/client/app/views/main.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
19 |
--------------------------------------------------------------------------------
/client/test/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "esnext": true,
5 | "bitwise": true,
6 | "camelcase": true,
7 | "curly": true,
8 | "eqeqeq": true,
9 | "immed": true,
10 | "indent": 2,
11 | "latedef": true,
12 | "newcap": true,
13 | "noarg": true,
14 | "quotmark": "single",
15 | "regexp": true,
16 | "undef": true,
17 | "unused": true,
18 | "strict": true,
19 | "trailing": true,
20 | "smarttabs": true,
21 | "globals": {
22 | "after": false,
23 | "afterEach": false,
24 | "angular": false,
25 | "before": false,
26 | "beforeEach": false,
27 | "browser": false,
28 | "describe": false,
29 | "expect": false,
30 | "inject": false,
31 | "it": false,
32 | "spyOn": false
33 | }
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/server/routes/camera.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var SonyCamera = require('../routes/sony-camera');
4 | var camera = new SonyCamera('10.0.0.1', 10000, '/sony/camera');
5 | camera.show();
6 |
7 | exports.call = function(req, res) {
8 | var method = req.params.method;
9 | var params = req.query.p ? JSON.parse(req.query.p) : null;
10 | // console.log(method);
11 | // console.log(params);
12 |
13 | camera.call(method, params, function (err, output) {
14 | if (err) {
15 | console.log(err);
16 | res.send(err);
17 | return;
18 | }
19 |
20 | res.send(output.result);
21 | });
22 | };
23 |
24 | exports.startViewfinder = function (req, res) {
25 | camera.startViewfinder(req, res);
26 | };
27 |
28 | exports.stopViewfinder = function (req, res) {
29 | camera.stopViewfinder(req, res);
30 | };
31 |
32 | exports.takePhoto = function (req, res) {
33 | camera.takePhoto(req, res);
34 | };
35 |
36 | exports.zoomIn = function (req, res) {
37 | camera.zoomIn(req, res);
38 | };
39 |
40 | exports.zoomOut = function (req, res) {
41 | camera.zoomOut(req, res);
42 | };
43 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Ikuo Terado
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/server/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Module dependencies.
5 | */
6 |
7 | var express = require('express');
8 | var routes = require('./routes');
9 | var camera = require('./routes/camera');
10 | var http = require('http');
11 | var path = require('path');
12 |
13 | var app = express();
14 |
15 | // all environments
16 | app.set('port', process.env.PORT || 3000);
17 | app.set('views', path.join(__dirname, 'views'));
18 | app.set('view engine', 'jade');
19 | app.use(express.favicon());
20 | app.use(express.logger('dev'));
21 | app.use(express.json());
22 | app.use(express.urlencoded());
23 | app.use(express.methodOverride());
24 | app.use(express.static(path.join(__dirname, 'public')));
25 | app.use(app.router);
26 |
27 | // development only
28 | if ('development' === app.get('env')) {
29 | app.use(express.errorHandler());
30 | }
31 |
32 | app.get('/', routes.index);
33 |
34 | app.get('/camera/viewfinder/start', camera.startViewfinder);
35 | app.get('/camera/viewfinder/stop', camera.stopViewfinder);
36 |
37 | app.get('/camera/photos/take', camera.takePhoto);
38 |
39 | app.get('/camera/zoom/in', camera.zoomIn);
40 | app.get('/camera/zoom/out', camera.zoomOut);
41 |
42 | app.get('/camera/:method/:params', camera.call);
43 | app.get('/camera/:method', camera.call);
44 | app.get('/camera', camera.call);
45 |
46 | http.createServer(app).listen(app.get('port'), function(){
47 | console.log('Express server listening on port ' + app.get('port'));
48 | });
49 |
--------------------------------------------------------------------------------
/client/app/scripts/controllers/main.js:
--------------------------------------------------------------------------------
1 | /* global $ */
2 |
3 | 'use strict';
4 |
5 | angular.module('clientApp')
6 | .controller('MainCtrl', function ($scope, $http) {
7 | $scope.photos = [];
8 |
9 | function resize() {
10 | var height = window.innerHeight;
11 | $('#liveview').height(height + 'px');
12 |
13 | var width = $('#liveview').width() || (height * 4 / 3);
14 | var left = (window.innerWidth - width) / 2;
15 | $('#liveview').css('left', left + 'px');
16 | }
17 | window.onresize = resize;
18 | resize();
19 |
20 | $scope.startLiveview = function () {
21 | $scope.liveview = '/camera/viewfinder/start';
22 | };
23 |
24 | $scope.stopLiveview = function () {
25 | call('viewfinder/stop');
26 | };
27 |
28 | $scope.takePhoto = function () {
29 | call('photos/take', null, function (result) {
30 | $scope.photos.push(result);
31 | });
32 | };
33 |
34 | $scope.zoomIn = function () {
35 | call('zoom/in');
36 | };
37 |
38 | $scope.zoomOut = function () {
39 | call('zoom/out');
40 | };
41 |
42 | function call (method, params, callback) {
43 | var CAMERA_API_URL = '/camera';
44 |
45 | params = params ? '?p=' + JSON.stringify(params) : '';
46 | var url = CAMERA_API_URL + '/' + method + params;
47 | $http.get(url).success(function (result) {
48 | // console.log(result);
49 | if (callback) {
50 | callback(result);
51 | }
52 | });
53 | }
54 |
55 | });
56 |
--------------------------------------------------------------------------------
/client/karma-e2e.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // http://karma-runner.github.io/0.10/config/configuration-file.html
3 |
4 | module.exports = function(config) {
5 | config.set({
6 | // base path, that will be used to resolve files and exclude
7 | basePath: '',
8 |
9 | // testing framework to use (jasmine/mocha/qunit/...)
10 | frameworks: ['ng-scenario'],
11 |
12 | // list of files / patterns to load in the browser
13 | files: [
14 | 'test/e2e/**/*.js'
15 | ],
16 |
17 | // list of files / patterns to exclude
18 | exclude: [],
19 |
20 | // web server port
21 | port: 8080,
22 |
23 | // level of logging
24 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
25 | logLevel: config.LOG_INFO,
26 |
27 |
28 | // enable / disable watching file and executing tests whenever any file changes
29 | autoWatch: false,
30 |
31 |
32 | // Start these browsers, currently available:
33 | // - Chrome
34 | // - ChromeCanary
35 | // - Firefox
36 | // - Opera
37 | // - Safari (only Mac)
38 | // - PhantomJS
39 | // - IE (only Windows)
40 | browsers: ['Chrome'],
41 |
42 |
43 | // Continuous Integration mode
44 | // if true, it capture browsers, run tests and exit
45 | singleRun: false
46 |
47 | // Uncomment the following lines if you are using grunt's server to run the tests
48 | // proxies: {
49 | // '/': 'http://localhost:9000/'
50 | // },
51 | // URL root prevent conflicts with the site root
52 | // urlRoot: '_karma_'
53 | });
54 | };
55 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // http://karma-runner.github.io/0.10/config/configuration-file.html
3 |
4 | module.exports = function(config) {
5 | config.set({
6 | // base path, that will be used to resolve files and exclude
7 | basePath: '',
8 |
9 | // testing framework to use (jasmine/mocha/qunit/...)
10 | frameworks: ['jasmine'],
11 |
12 | // list of files / patterns to load in the browser
13 | files: [
14 | 'app/bower_components/angular/angular.js',
15 | 'app/bower_components/angular-mocks/angular-mocks.js',
16 | 'app/bower_components/angular-resource/angular-resource.js',
17 | 'app/bower_components/angular-cookies/angular-cookies.js',
18 | 'app/bower_components/angular-sanitize/angular-sanitize.js',
19 | 'app/bower_components/angular-route/angular-route.js',
20 | 'app/scripts/*.js',
21 | 'app/scripts/**/*.js',
22 | 'test/mock/**/*.js',
23 | 'test/spec/**/*.js'
24 | ],
25 |
26 | // list of files / patterns to exclude
27 | exclude: [],
28 |
29 | // web server port
30 | port: 8080,
31 |
32 | // level of logging
33 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
34 | logLevel: config.LOG_INFO,
35 |
36 |
37 | // enable / disable watching file and executing tests whenever any file changes
38 | autoWatch: false,
39 |
40 |
41 | // Start these browsers, currently available:
42 | // - Chrome
43 | // - ChromeCanary
44 | // - Firefox
45 | // - Opera
46 | // - Safari (only Mac)
47 | // - PhantomJS
48 | // - IE (only Windows)
49 | browsers: ['Chrome'],
50 |
51 |
52 | // Continuous Integration mode
53 | // if true, it capture browsers, run tests and exit
54 | singleRun: false
55 | });
56 | };
57 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "RemoteCamera",
3 | "version": "0.0.1",
4 | "dependencies": {
5 | "express": "3.4.8",
6 | "jade": "~1.1.4",
7 | "ahr2": "~2.3.2"
8 | },
9 | "devDependencies": {
10 | "grunt": "~0.4.1",
11 | "grunt-autoprefixer": "~0.6.4",
12 | "grunt-concurrent": "~0.4.1",
13 | "grunt-contrib-clean": "~0.5.0",
14 | "grunt-contrib-coffee": "~0.8.0",
15 | "grunt-contrib-compass": "~0.7.0",
16 | "grunt-contrib-concat": "~0.3.0",
17 | "grunt-contrib-connect": "~0.6.0",
18 | "grunt-contrib-copy": "~0.5.0",
19 | "grunt-contrib-cssmin": "~0.7.0",
20 | "grunt-contrib-htmlmin": "~0.1.3",
21 | "grunt-contrib-imagemin": "~0.5.0",
22 | "grunt-contrib-jshint": "~0.8.0",
23 | "grunt-contrib-uglify": "~0.2.0",
24 | "grunt-contrib-watch": "~0.5.2",
25 | "grunt-google-cdn": "~0.2.0",
26 | "grunt-newer": "~0.6.1",
27 | "grunt-ngmin": "~0.0.2",
28 | "grunt-rev": "~0.1.0",
29 | "grunt-svgmin": "~0.3.0",
30 | "grunt-usemin": "~2.0.0",
31 | "jshint-stylish": "~0.1.5",
32 | "load-grunt-tasks": "~0.2.0",
33 | "time-grunt": "~0.2.8",
34 | "karma-ng-scenario": "~0.1.0",
35 | "grunt-karma": "~0.6.2",
36 | "karma-script-launcher": "~0.1.0",
37 | "karma-chrome-launcher": "~0.1.1",
38 | "karma-html2js-preprocessor": "~0.1.0",
39 | "karma-firefox-launcher": "~0.1.3",
40 | "karma-jasmine": "~0.1.5",
41 | "karma-coffee-preprocessor": "~0.1.2",
42 | "requirejs": "~2.1.10",
43 | "karma-requirejs": "~0.2.0",
44 | "karma-phantomjs-launcher": "~0.1.1",
45 | "karma": "~0.10.9",
46 | "karma-ng-html2js-preprocessor": "~0.1.0"
47 | },
48 | "engines": {
49 | "node": ">=0.8.0"
50 | },
51 | "scripts": {
52 | "start": "NODE_ENV=development node-dev server/app.js"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RemoteCamera
2 |
3 | [](https://travis-ci.org/eqot/RemoteCamera)
4 | [](https://gemnasium.com/eqot/RemoteCamera)
5 | [](https://codeclimate.com/github/eqot/RemoteCamera)
6 | [](https://coveralls.io/r/eqot/RemoteCamera)
7 |
8 | This aims to communicate with Sony camera through [Camera Remote API SDK](http://developer.sony.com/develop/cameras/) from web app which is implemented in JavaScript.
9 |
10 |
11 | ## Technical details
12 |
13 | This contains two parts, server- and client-side code.
14 |
15 | Server-side code is implemented for client to communicate with Sony camera.
16 | Since Camera Remote API does not support [CORS](http://www.w3.org/TR/cors/) unfortunately, client-side code in browser cannot access to Sony camera directly. So this server-side code helps for API call from client to Sony camera to be bypassed.
17 | Also the server supports that liveview data from Sony camera is converted into motion JPEG so that the client can easily show the liveview in HTML file.
18 |
19 | Client-side code is a main part which has UI widgets for controlling Sony camera and showing liveview.
20 |
21 |
22 | ## How to use
23 |
24 | ```
25 | git clone https://github.com/eqot/RemoteCamera.git
26 | cd RemoteCamera
27 | npm install && bower install
28 | grunt build
29 | npm start
30 | ```
31 |
32 |
33 | ## Limitations
34 |
35 | * Since this has not supported device discovery yet, ip address and port number for Sony camera is hard-coded for [DSC-QX100](http://developer.sony.com/devices/cameras/sony-smartphone-attachable-lens-style-camera-dsc-qx100/)
36 |
37 |
38 | ## License
39 |
40 | Copyright © 2013-2014 Ikuo Terado. Released under the [MIT license](http://www.opensource.org/licenses/mit-license.php).
41 |
--------------------------------------------------------------------------------
/client/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
22 |
23 |
27 |
28 |
29 |
30 |
31 |
32 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/client/app/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Page Not Found :(
6 |
141 |
142 |
143 |
144 |
Not found :(
145 |
Sorry, but the page you were trying to view does not exist.
146 |
It looks like this was the result of either:
147 |
148 | - a mistyped address
149 | - an out-of-date link
150 |
151 |
154 |
155 |
156 |
157 |
158 |
--------------------------------------------------------------------------------
/server/routes/sony-camera.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var fs = require('fs');
4 | var url = require('url');
5 | var http = require('http');
6 | var request = require('ahr2');
7 |
8 | (function () {
9 |
10 | var SonyCamera = function (url, port, path) {
11 | this.url = url || '10.0.0.1';
12 | this.port = port || 10000;
13 | this.path = path || '/sony/camera';
14 |
15 | this.rpcReq = {
16 | id: 1,
17 | version: '1.0'
18 | };
19 |
20 | this.publicDir = require('path').join(__dirname, '../public/');
21 | this.photoDir = 'camera/photos/';
22 | };
23 |
24 | SonyCamera.prototype.show = function () {
25 | console.log(this.url + ':' + this.port + this.path);
26 | };
27 |
28 | SonyCamera.prototype.call = function (method, params, callback) {
29 | this.rpcReq.method = method;
30 | this.rpcReq.params = params || [];
31 | console.log(this.rpcReq);
32 |
33 | request({
34 | method: 'POST',
35 | hostname: this.url,
36 | port: this.port,
37 | pathname: this.path,
38 | body: JSON.stringify(this.rpcReq)
39 | }).when(function (err, ahr, rpcRes) {
40 | if (callback) {
41 | callback(err, rpcRes);
42 | }
43 | });
44 | };
45 |
46 | SonyCamera.prototype.waitUntilIdle = function (callback) {
47 | this.call('getEvent', [false], function (err, result) {
48 | // console.log(err);
49 | if (!err) {
50 | var cameraStatus = result.result[1].cameraStatus;
51 | // console.log(cameraStatus);
52 | if (cameraStatus !== 'IDLE') {
53 | err = 'NOT_IDLE';
54 | }
55 | }
56 |
57 | if (callback) {
58 | callback(err);
59 | }
60 | });
61 | };
62 |
63 | SonyCamera.prototype.startViewfinder = function (req, res) {
64 | this.call('startLiveview', null, function (err, output) {
65 | var liveviewUrl = url.parse(output.result[0]);
66 | // console.log(liveviewUrl);
67 |
68 | var COMMON_HEADER_SIZE = 8;
69 | var PAYLOAD_HEADER_SIZE = 128;
70 | var JPEG_SIZE_POSITION = 4;
71 | var PADDING_SIZE_POSITION = 7;
72 |
73 | var jpegSize = 0;
74 | var paddingSize = 0;
75 |
76 | var boundary = 'boundary';
77 |
78 | var liveviewReq = http.request(liveviewUrl, function (liveviewRes) {
79 | // console.log(data);
80 |
81 | var contentType = liveviewRes.headers['content-type'];
82 | var boundaryBuffer = new Buffer('\n--' + boundary + '\nContent-Type: ' + contentType + '\n\n');
83 |
84 | var buffer = new Buffer(0);
85 |
86 | res.writeHead(200, {
87 | 'Expires': 'Mon, 01 Jul 1980 00:00:00 GMT',
88 | 'Cache-Control': 'no-cache, no-store, must-revalidate',
89 | 'Pragma': 'no-cache',
90 | 'Content-Type': 'multipart/x-mixed-replace;boundary=' + boundary
91 | });
92 |
93 | liveviewRes.on('data', function (chunk) {
94 | if (jpegSize === 0) {
95 | buffer = Buffer.concat([buffer, chunk]);
96 |
97 | if (buffer.length >= (COMMON_HEADER_SIZE + PAYLOAD_HEADER_SIZE)) {
98 | jpegSize =
99 | buffer.readUInt8(COMMON_HEADER_SIZE + JPEG_SIZE_POSITION) * 65536 +
100 | buffer.readUInt16BE(COMMON_HEADER_SIZE + JPEG_SIZE_POSITION + 1);
101 | // console.log(jpegSize);
102 |
103 | paddingSize = buffer.readUInt8(COMMON_HEADER_SIZE + PADDING_SIZE_POSITION);
104 | // console.log(paddingSize);
105 |
106 | res.write(boundaryBuffer);
107 |
108 | buffer = buffer.slice(8 + 128);
109 | if (buffer.length > 0) {
110 | res.write(buffer);
111 | }
112 | }
113 | } else {
114 | res.write(chunk);
115 |
116 | if (chunk.length < jpegSize) {
117 | jpegSize -= chunk.length;
118 | } else {
119 | buffer = chunk.slice(jpegSize + paddingSize);
120 | jpegSize = 0;
121 | }
122 | }
123 | });
124 |
125 | liveviewRes.on('end', function () {
126 | console.log('End');
127 | });
128 |
129 | liveviewRes.on('close', function () {
130 | console.log('Close');
131 | });
132 | });
133 |
134 | liveviewReq.on('error', function(e) {
135 | console.error('Error: ', e);
136 | });
137 |
138 | liveviewReq.end();
139 | });
140 | };
141 |
142 | SonyCamera.prototype.stopViewfinder = function (req, res) {
143 | this.call('stopLiveview');
144 | res.send(null);
145 | };
146 |
147 | SonyCamera.prototype.takePhoto = function (req, res) {
148 | var self = this;
149 |
150 | this.waitUntilIdle(function (err) {
151 | if (err) {
152 | res.send(null);
153 | return;
154 | }
155 |
156 | self.call('actTakePicture', null, function (err, output) {
157 | var url = output.result[0][0];
158 |
159 | request.get(url).when(function (err, ahr, data) {
160 | var photoName = url.split('?')[0].split('/')[3];
161 | // console.log(photoName);
162 |
163 | fs.writeFile(self.publicDir + self.photoDir + photoName, data, function (err) {
164 | if (err) {
165 | throw err;
166 | }
167 |
168 | res.send(self.photoDir + photoName);
169 | });
170 | });
171 | });
172 | });
173 | };
174 |
175 | SonyCamera.prototype.zoomIn = function (req, res) {
176 | this.call('actZoom', ['in', 'start']);
177 | res.send(null);
178 | };
179 |
180 | SonyCamera.prototype.zoomOut = function (req, res) {
181 | this.call('actZoom', ['out', 'start']);
182 | res.send(null);
183 | };
184 |
185 |
186 | // Client-side export
187 | if (typeof window !== 'undefined' && window.SonyCamera) { window.SonyCamera = SonyCamera; }
188 | // Server-side export
189 | if (typeof module !== 'undefined') { module.exports = SonyCamera; }
190 |
191 | }());
192 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | // Generated on 2013-12-21 using generator-angular 0.6.0
2 | 'use strict';
3 |
4 | // # Globbing
5 | // for performance reasons we're only matching one level down:
6 | // 'test/spec/{,*/}*.js'
7 | // use this if you want to recursively match all subfolders:
8 | // 'test/spec/**/*.js'
9 |
10 | module.exports = function (grunt) {
11 |
12 | // Load grunt tasks automatically
13 | require('load-grunt-tasks')(grunt);
14 |
15 | // Time how long tasks take. Can help when optimizing build times
16 | require('time-grunt')(grunt);
17 |
18 | // Define the configuration for all the tasks
19 | grunt.initConfig({
20 |
21 | // Project settings
22 | yeoman: {
23 | // configurable paths
24 | app: require('./bower.json').appPath || 'app',
25 | dist: require('./bower.json').distPath || 'dist'
26 | },
27 |
28 | // Watches files for changes and runs tasks based on the changed files
29 | watch: {
30 | js: {
31 | files: ['{.tmp,<%= yeoman.app %>}/scripts/{,*/}*.js'],
32 | tasks: ['newer:jshint:all']
33 | },
34 | jsTest: {
35 | files: ['test/spec/{,*/}*.js'],
36 | tasks: ['newer:jshint:test', 'karma']
37 | },
38 | compass: {
39 | files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
40 | tasks: ['compass:server', 'autoprefixer']
41 | },
42 | styles: {
43 | files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
44 | tasks: ['newer:copy:styles', 'autoprefixer']
45 | },
46 | gruntfile: {
47 | files: ['Gruntfile.js']
48 | },
49 | livereload: {
50 | options: {
51 | livereload: '<%= connect.options.livereload %>'
52 | },
53 | files: [
54 | '<%= yeoman.app %>/{,*/}*.html',
55 | '.tmp/styles/{,*/}*.css',
56 | '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
57 | ]
58 | }
59 | },
60 |
61 | // The actual grunt server settings
62 | connect: {
63 | options: {
64 | port: 9000,
65 | // Change this to '0.0.0.0' to access the server from outside.
66 | hostname: 'localhost',
67 | livereload: 35729
68 | },
69 | livereload: {
70 | options: {
71 | open: true,
72 | base: [
73 | '.tmp',
74 | '<%= yeoman.app %>'
75 | ]
76 | }
77 | },
78 | test: {
79 | options: {
80 | port: 9001,
81 | base: [
82 | '.tmp',
83 | 'test',
84 | '<%= yeoman.app %>'
85 | ]
86 | }
87 | },
88 | dist: {
89 | options: {
90 | base: '<%= yeoman.dist %>'
91 | }
92 | }
93 | },
94 |
95 | // Make sure code styles are up to par and there are no obvious mistakes
96 | jshint: {
97 | options: {
98 | jshintrc: '.jshintrc',
99 | reporter: require('jshint-stylish')
100 | },
101 | all: [
102 | 'Gruntfile.js',
103 | '<%= yeoman.app %>/scripts/{,*/}*.js',
104 | 'server/{,*/}*.js'
105 | ],
106 | test: {
107 | options: {
108 | jshintrc: 'client/test/.jshintrc'
109 | },
110 | src: ['client/test/spec/{,*/}*.js']
111 | }
112 | },
113 |
114 | // Empties folders to start fresh
115 | clean: {
116 | dist: {
117 | files: [{
118 | dot: true,
119 | src: [
120 | '.tmp',
121 | '<%= yeoman.dist %>/*',
122 | '!<%= yeoman.dist %>/.git*'
123 | ]
124 | }]
125 | },
126 | server: '.tmp'
127 | },
128 |
129 | // Add vendor prefixed styles
130 | autoprefixer: {
131 | options: {
132 | browsers: ['last 1 version']
133 | },
134 | dist: {
135 | files: [{
136 | expand: true,
137 | cwd: '.tmp/styles/',
138 | src: '{,*/}*.css',
139 | dest: '.tmp/styles/'
140 | }]
141 | }
142 | },
143 |
144 |
145 |
146 |
147 | // Compiles Sass to CSS and generates necessary files if requested
148 | compass: {
149 | options: {
150 | sassDir: '<%= yeoman.app %>/styles',
151 | cssDir: '.tmp/styles',
152 | generatedImagesDir: '.tmp/images/generated',
153 | imagesDir: '<%= yeoman.app %>/images',
154 | javascriptsDir: '<%= yeoman.app %>/scripts',
155 | fontsDir: '<%= yeoman.app %>/styles/fonts',
156 | importPath: '<%= yeoman.app %>/bower_components',
157 | httpImagesPath: '/images',
158 | httpGeneratedImagesPath: '/images/generated',
159 | httpFontsPath: '/styles/fonts',
160 | relativeAssets: false,
161 | assetCacheBuster: false
162 | },
163 | dist: {
164 | options: {
165 | generatedImagesDir: '<%= yeoman.dist %>/images/generated'
166 | }
167 | },
168 | server: {
169 | options: {
170 | debugInfo: true
171 | }
172 | }
173 | },
174 |
175 | // Renames files for browser caching purposes
176 | rev: {
177 | dist: {
178 | files: {
179 | src: [
180 | '<%= yeoman.dist %>/scripts/{,*/}*.js',
181 | '<%= yeoman.dist %>/styles/{,*/}*.css',
182 | '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
183 | '<%= yeoman.dist %>/styles/fonts/*'
184 | ]
185 | }
186 | }
187 | },
188 |
189 | // Reads HTML for usemin blocks to enable smart builds that automatically
190 | // concat, minify and revision files. Creates configurations in memory so
191 | // additional tasks can operate on them
192 | useminPrepare: {
193 | html: '<%= yeoman.app %>/index.html',
194 | options: {
195 | dest: '<%= yeoman.dist %>'
196 | }
197 | },
198 |
199 | // Performs rewrites based on rev and the useminPrepare configuration
200 | usemin: {
201 | html: ['<%= yeoman.dist %>/{,*/}*.html'],
202 | css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
203 | options: {
204 | assetsDirs: ['<%= yeoman.dist %>']
205 | }
206 | },
207 |
208 | // The following *-min tasks produce minified files in the dist folder
209 | imagemin: {
210 | dist: {
211 | files: [{
212 | expand: true,
213 | cwd: '<%= yeoman.app %>/images',
214 | src: '{,*/}*.{png,jpg,jpeg,gif}',
215 | dest: '<%= yeoman.dist %>/images'
216 | }]
217 | }
218 | },
219 | svgmin: {
220 | dist: {
221 | files: [{
222 | expand: true,
223 | cwd: '<%= yeoman.app %>/images',
224 | src: '{,*/}*.svg',
225 | dest: '<%= yeoman.dist %>/images'
226 | }]
227 | }
228 | },
229 | htmlmin: {
230 | dist: {
231 | options: {
232 | // Optional configurations that you can uncomment to use
233 | // removeCommentsFromCDATA: true,
234 | // collapseBooleanAttributes: true,
235 | // removeAttributeQuotes: true,
236 | // removeRedundantAttributes: true,
237 | // useShortDoctype: true,
238 | // removeEmptyAttributes: true,
239 | // removeOptionalTags: true*/
240 | },
241 | files: [{
242 | expand: true,
243 | cwd: '<%= yeoman.app %>',
244 | src: ['*.html', 'views/*.html'],
245 | dest: '<%= yeoman.dist %>'
246 | }]
247 | }
248 | },
249 |
250 | // Allow the use of non-minsafe AngularJS files. Automatically makes it
251 | // minsafe compatible so Uglify does not destroy the ng references
252 | ngmin: {
253 | dist: {
254 | files: [{
255 | expand: true,
256 | cwd: '.tmp/concat/scripts',
257 | src: '*.js',
258 | dest: '.tmp/concat/scripts'
259 | }]
260 | }
261 | },
262 |
263 | // Replace Google CDN references
264 | cdnify: {
265 | dist: {
266 | html: ['<%= yeoman.dist %>/*.html']
267 | }
268 | },
269 |
270 | // Copies remaining files to places other tasks can use
271 | copy: {
272 | dist: {
273 | files: [{
274 | expand: true,
275 | dot: true,
276 | cwd: '<%= yeoman.app %>',
277 | dest: '<%= yeoman.dist %>',
278 | src: [
279 | '*.{ico,png,txt}',
280 | '.htaccess',
281 | 'bower_components/**/*',
282 | 'images/{,*/}*.{webp}',
283 | 'fonts/*'
284 | ]
285 | }, {
286 | expand: true,
287 | cwd: '.tmp/images',
288 | dest: '<%= yeoman.dist %>/images',
289 | src: [
290 | 'generated/*'
291 | ]
292 | }]
293 | },
294 | styles: {
295 | // expand: true,
296 | // cwd: '<%= yeoman.app %>/styles',
297 | // dest: '.tmp/styles/',
298 | // src: '{,*/}*.css'
299 | }
300 | },
301 |
302 | // Run some tasks in parallel to speed up the build process
303 | concurrent: {
304 | server: [
305 | 'compass:server',
306 | 'copy:styles'
307 | ],
308 | test: [
309 | 'compass',
310 | 'copy:styles'
311 | ],
312 | dist: [
313 | 'compass:dist',
314 | 'copy:styles',
315 | 'imagemin',
316 | 'svgmin',
317 | 'htmlmin'
318 | ]
319 | },
320 |
321 | // By default, your `index.html`'s will take care of
322 | // minification. These next options are pre-configured if you do not wish
323 | // to use the Usemin blocks.
324 | // cssmin: {
325 | // dist: {
326 | // files: {
327 | // '<%= yeoman.dist %>/styles/main.css': [
328 | // '.tmp/styles/{,*/}*.css',
329 | // '<%= yeoman.app %>/styles/{,*/}*.css'
330 | // ]
331 | // }
332 | // }
333 | // },
334 | // uglify: {
335 | // dist: {
336 | // files: {
337 | // '<%= yeoman.dist %>/scripts/scripts.js': [
338 | // '<%= yeoman.dist %>/scripts/scripts.js'
339 | // ]
340 | // }
341 | // }
342 | // },
343 | // concat: {
344 | // dist: {}
345 | // },
346 |
347 | // Test settings
348 | karma: {
349 | unit: {
350 | configFile: 'karma.conf.js',
351 | singleRun: true
352 | }
353 | }
354 | });
355 |
356 |
357 | grunt.registerTask('serve', function (target) {
358 | if (target === 'dist') {
359 | return grunt.task.run(['build', 'connect:dist:keepalive']);
360 | }
361 |
362 | grunt.task.run([
363 | 'clean:server',
364 | 'concurrent:server',
365 | 'autoprefixer',
366 | 'connect:livereload',
367 | 'watch'
368 | ]);
369 | });
370 |
371 | grunt.registerTask('server', function () {
372 | grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
373 | grunt.task.run(['serve']);
374 | });
375 |
376 | grunt.registerTask('test', [
377 | 'clean:server',
378 | 'concurrent:test',
379 | 'autoprefixer',
380 | 'connect:test',
381 | 'karma'
382 | ]);
383 |
384 | grunt.registerTask('build', [
385 | 'clean:dist',
386 | 'useminPrepare',
387 | 'concurrent:dist',
388 | 'autoprefixer',
389 | 'concat',
390 | 'ngmin',
391 | 'copy:dist',
392 | 'cdnify',
393 | 'cssmin',
394 | 'uglify',
395 | // 'rev',
396 | 'usemin'
397 | ]);
398 |
399 | grunt.registerTask('default', [
400 | 'newer:jshint',
401 | 'test',
402 | 'build'
403 | ]);
404 | };
405 |
--------------------------------------------------------------------------------
/client/app/.htaccess:
--------------------------------------------------------------------------------
1 | # Apache Configuration File
2 |
3 | # (!) Using `.htaccess` files slows down Apache, therefore, if you have access
4 | # to the main server config file (usually called `httpd.conf`), you should add
5 | # this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html.
6 |
7 | # ##############################################################################
8 | # # CROSS-ORIGIN RESOURCE SHARING (CORS) #
9 | # ##############################################################################
10 |
11 | # ------------------------------------------------------------------------------
12 | # | Cross-domain AJAX requests |
13 | # ------------------------------------------------------------------------------
14 |
15 | # Enable cross-origin AJAX requests.
16 | # http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity
17 | # http://enable-cors.org/
18 |
19 | #
20 | # Header set Access-Control-Allow-Origin "*"
21 | #
22 |
23 | # ------------------------------------------------------------------------------
24 | # | CORS-enabled images |
25 | # ------------------------------------------------------------------------------
26 |
27 | # Send the CORS header for images when browsers request it.
28 | # https://developer.mozilla.org/en/CORS_Enabled_Image
29 | # http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html
30 | # http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/
31 |
32 |
33 |
34 |
35 | SetEnvIf Origin ":" IS_CORS
36 | Header set Access-Control-Allow-Origin "*" env=IS_CORS
37 |
38 |
39 |
40 |
41 | # ------------------------------------------------------------------------------
42 | # | Web fonts access |
43 | # ------------------------------------------------------------------------------
44 |
45 | # Allow access from all domains for web fonts
46 |
47 |
48 |
49 | Header set Access-Control-Allow-Origin "*"
50 |
51 |
52 |
53 |
54 | # ##############################################################################
55 | # # ERRORS #
56 | # ##############################################################################
57 |
58 | # ------------------------------------------------------------------------------
59 | # | 404 error prevention for non-existing redirected folders |
60 | # ------------------------------------------------------------------------------
61 |
62 | # Prevent Apache from returning a 404 error for a rewrite if a directory
63 | # with the same name does not exist.
64 | # http://httpd.apache.org/docs/current/content-negotiation.html#multiviews
65 | # http://www.webmasterworld.com/apache/3808792.htm
66 |
67 | Options -MultiViews
68 |
69 | # ------------------------------------------------------------------------------
70 | # | Custom error messages / pages |
71 | # ------------------------------------------------------------------------------
72 |
73 | # You can customize what Apache returns to the client in case of an error (see
74 | # http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.:
75 |
76 | ErrorDocument 404 /404.html
77 |
78 |
79 | # ##############################################################################
80 | # # INTERNET EXPLORER #
81 | # ##############################################################################
82 |
83 | # ------------------------------------------------------------------------------
84 | # | Better website experience |
85 | # ------------------------------------------------------------------------------
86 |
87 | # Force IE to render pages in the highest available mode in the various
88 | # cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf.
89 |
90 |
91 | Header set X-UA-Compatible "IE=edge"
92 | # `mod_headers` can't match based on the content-type, however, we only
93 | # want to send this header for HTML pages and not for the other resources
94 |
95 | Header unset X-UA-Compatible
96 |
97 |
98 |
99 | # ------------------------------------------------------------------------------
100 | # | Cookie setting from iframes |
101 | # ------------------------------------------------------------------------------
102 |
103 | # Allow cookies to be set from iframes in IE.
104 |
105 | #
106 | # Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\""
107 | #
108 |
109 | # ------------------------------------------------------------------------------
110 | # | Screen flicker |
111 | # ------------------------------------------------------------------------------
112 |
113 | # Stop screen flicker in IE on CSS rollovers (this only works in
114 | # combination with the `ExpiresByType` directives for images from below).
115 |
116 | # BrowserMatch "MSIE" brokenvary=1
117 | # BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1
118 | # BrowserMatch "Opera" !brokenvary
119 | # SetEnvIf brokenvary 1 force-no-vary
120 |
121 |
122 | # ##############################################################################
123 | # # MIME TYPES AND ENCODING #
124 | # ##############################################################################
125 |
126 | # ------------------------------------------------------------------------------
127 | # | Proper MIME types for all files |
128 | # ------------------------------------------------------------------------------
129 |
130 |
131 |
132 | # Audio
133 | AddType audio/mp4 m4a f4a f4b
134 | AddType audio/ogg oga ogg
135 |
136 | # JavaScript
137 | # Normalize to standard type (it's sniffed in IE anyways):
138 | # http://tools.ietf.org/html/rfc4329#section-7.2
139 | AddType application/javascript js jsonp
140 | AddType application/json json
141 |
142 | # Video
143 | AddType video/mp4 mp4 m4v f4v f4p
144 | AddType video/ogg ogv
145 | AddType video/webm webm
146 | AddType video/x-flv flv
147 |
148 | # Web fonts
149 | AddType application/font-woff woff
150 | AddType application/vnd.ms-fontobject eot
151 |
152 | # Browsers usually ignore the font MIME types and sniff the content,
153 | # however, Chrome shows a warning if other MIME types are used for the
154 | # following fonts.
155 | AddType application/x-font-ttf ttc ttf
156 | AddType font/opentype otf
157 |
158 | # Make SVGZ fonts work on iPad:
159 | # https://twitter.com/FontSquirrel/status/14855840545
160 | AddType image/svg+xml svg svgz
161 | AddEncoding gzip svgz
162 |
163 | # Other
164 | AddType application/octet-stream safariextz
165 | AddType application/x-chrome-extension crx
166 | AddType application/x-opera-extension oex
167 | AddType application/x-shockwave-flash swf
168 | AddType application/x-web-app-manifest+json webapp
169 | AddType application/x-xpinstall xpi
170 | AddType application/xml atom rdf rss xml
171 | AddType image/webp webp
172 | AddType image/x-icon ico
173 | AddType text/cache-manifest appcache manifest
174 | AddType text/vtt vtt
175 | AddType text/x-component htc
176 | AddType text/x-vcard vcf
177 |
178 |
179 |
180 | # ------------------------------------------------------------------------------
181 | # | UTF-8 encoding |
182 | # ------------------------------------------------------------------------------
183 |
184 | # Use UTF-8 encoding for anything served as `text/html` or `text/plain`.
185 | AddDefaultCharset utf-8
186 |
187 | # Force UTF-8 for certain file formats.
188 |
189 | AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml
190 |
191 |
192 |
193 | # ##############################################################################
194 | # # URL REWRITES #
195 | # ##############################################################################
196 |
197 | # ------------------------------------------------------------------------------
198 | # | Rewrite engine |
199 | # ------------------------------------------------------------------------------
200 |
201 | # Turning on the rewrite engine and enabling the `FollowSymLinks` option is
202 | # necessary for the following directives to work.
203 |
204 | # If your web host doesn't allow the `FollowSymlinks` option, you may need to
205 | # comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the
206 | # performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks
207 |
208 | # Also, some cloud hosting services require `RewriteBase` to be set:
209 | # http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site
210 |
211 |
212 | Options +FollowSymlinks
213 | # Options +SymLinksIfOwnerMatch
214 | RewriteEngine On
215 | # RewriteBase /
216 |
217 |
218 | # ------------------------------------------------------------------------------
219 | # | Suppressing / Forcing the "www." at the beginning of URLs |
220 | # ------------------------------------------------------------------------------
221 |
222 | # The same content should never be available under two different URLs especially
223 | # not with and without "www." at the beginning. This can cause SEO problems
224 | # (duplicate content), therefore, you should choose one of the alternatives and
225 | # redirect the other one.
226 |
227 | # By default option 1 (no "www.") is activated:
228 | # http://no-www.org/faq.php?q=class_b
229 |
230 | # If you'd prefer to use option 2, just comment out all the lines from option 1
231 | # and uncomment the ones from option 2.
232 |
233 | # IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME!
234 |
235 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
236 |
237 | # Option 1: rewrite www.example.com → example.com
238 |
239 |
240 | RewriteCond %{HTTPS} !=on
241 | RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
242 | RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
243 |
244 |
245 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
246 |
247 | # Option 2: rewrite example.com → www.example.com
248 |
249 | # Be aware that the following might not be a good idea if you use "real"
250 | # subdomains for certain parts of your website.
251 |
252 | #
253 | # RewriteCond %{HTTPS} !=on
254 | # RewriteCond %{HTTP_HOST} !^www\..+$ [NC]
255 | # RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
256 | #
257 |
258 |
259 | # ##############################################################################
260 | # # SECURITY #
261 | # ##############################################################################
262 |
263 | # ------------------------------------------------------------------------------
264 | # | Content Security Policy (CSP) |
265 | # ------------------------------------------------------------------------------
266 |
267 | # You can mitigate the risk of cross-site scripting and other content-injection
268 | # attacks by setting a Content Security Policy which whitelists trusted sources
269 | # of content for your site.
270 |
271 | # The example header below allows ONLY scripts that are loaded from the current
272 | # site's origin (no inline scripts, no CDN, etc). This almost certainly won't
273 | # work as-is for your site!
274 |
275 | # To get all the details you'll need to craft a reasonable policy for your site,
276 | # read: http://html5rocks.com/en/tutorials/security/content-security-policy (or
277 | # see the specification: http://w3.org/TR/CSP).
278 |
279 | #
280 | # Header set Content-Security-Policy "script-src 'self'; object-src 'self'"
281 | #
282 | # Header unset Content-Security-Policy
283 | #
284 | #
285 |
286 | # ------------------------------------------------------------------------------
287 | # | File access |
288 | # ------------------------------------------------------------------------------
289 |
290 | # Block access to directories without a default document.
291 | # Usually you should leave this uncommented because you shouldn't allow anyone
292 | # to surf through every directory on your server (which may includes rather
293 | # private places like the CMS's directories).
294 |
295 |
296 | Options -Indexes
297 |
298 |
299 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
300 |
301 | # Block access to hidden files and directories.
302 | # This includes directories used by version control systems such as Git and SVN.
303 |
304 |
305 | RewriteCond %{SCRIPT_FILENAME} -d [OR]
306 | RewriteCond %{SCRIPT_FILENAME} -f
307 | RewriteRule "(^|/)\." - [F]
308 |
309 |
310 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
311 |
312 | # Block access to backup and source files.
313 | # These files may be left by some text editors and can pose a great security
314 | # danger when anyone has access to them.
315 |
316 |
317 | Order allow,deny
318 | Deny from all
319 | Satisfy All
320 |
321 |
322 | # ------------------------------------------------------------------------------
323 | # | Secure Sockets Layer (SSL) |
324 | # ------------------------------------------------------------------------------
325 |
326 | # Rewrite secure requests properly to prevent SSL certificate warnings, e.g.:
327 | # prevent `https://www.example.com` when your certificate only allows
328 | # `https://secure.example.com`.
329 |
330 | #
331 | # RewriteCond %{SERVER_PORT} !^443
332 | # RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L]
333 | #
334 |
335 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
336 |
337 | # Force client-side SSL redirection.
338 |
339 | # If a user types "example.com" in his browser, the above rule will redirect him
340 | # to the secure version of the site. That still leaves a window of opportunity
341 | # (the initial HTTP connection) for an attacker to downgrade or redirect the
342 | # request. The following header ensures that browser will ONLY connect to your
343 | # server via HTTPS, regardless of what the users type in the address bar.
344 | # http://www.html5rocks.com/en/tutorials/security/transport-layer-security/
345 |
346 | #
347 | # Header set Strict-Transport-Security max-age=16070400;
348 | #
349 |
350 | # ------------------------------------------------------------------------------
351 | # | Server software information |
352 | # ------------------------------------------------------------------------------
353 |
354 | # Avoid displaying the exact Apache version number, the description of the
355 | # generic OS-type and the information about Apache's compiled-in modules.
356 |
357 | # ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`!
358 |
359 | # ServerTokens Prod
360 |
361 |
362 | # ##############################################################################
363 | # # WEB PERFORMANCE #
364 | # ##############################################################################
365 |
366 | # ------------------------------------------------------------------------------
367 | # | Compression |
368 | # ------------------------------------------------------------------------------
369 |
370 |
371 |
372 | # Force compression for mangled headers.
373 | # http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping
374 |
375 |
376 | SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
377 | RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
378 |
379 |
380 |
381 | # Compress all output labeled with one of the following MIME-types
382 | # (for Apache versions below 2.3.7, you don't need to enable `mod_filter`
383 | # and can remove the `` and `` lines
384 | # as `AddOutputFilterByType` is still in the core directives).
385 |
386 | AddOutputFilterByType DEFLATE application/atom+xml \
387 | application/javascript \
388 | application/json \
389 | application/rss+xml \
390 | application/vnd.ms-fontobject \
391 | application/x-font-ttf \
392 | application/x-web-app-manifest+json \
393 | application/xhtml+xml \
394 | application/xml \
395 | font/opentype \
396 | image/svg+xml \
397 | image/x-icon \
398 | text/css \
399 | text/html \
400 | text/plain \
401 | text/x-component \
402 | text/xml
403 |
404 |
405 |
406 |
407 | # ------------------------------------------------------------------------------
408 | # | Content transformations |
409 | # ------------------------------------------------------------------------------
410 |
411 | # Prevent some of the mobile network providers from modifying the content of
412 | # your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5.
413 |
414 | #
415 | # Header set Cache-Control "no-transform"
416 | #
417 |
418 | # ------------------------------------------------------------------------------
419 | # | ETag removal |
420 | # ------------------------------------------------------------------------------
421 |
422 | # Since we're sending far-future expires headers (see below), ETags can
423 | # be removed: http://developer.yahoo.com/performance/rules.html#etags.
424 |
425 | # `FileETag None` is not enough for every server.
426 |
427 | Header unset ETag
428 |
429 |
430 | FileETag None
431 |
432 | # ------------------------------------------------------------------------------
433 | # | Expires headers (for better cache control) |
434 | # ------------------------------------------------------------------------------
435 |
436 | # The following expires headers are set pretty far in the future. If you don't
437 | # control versioning with filename-based cache busting, consider lowering the
438 | # cache time for resources like CSS and JS to something like 1 week.
439 |
440 |
441 |
442 | ExpiresActive on
443 | ExpiresDefault "access plus 1 month"
444 |
445 | # CSS
446 | ExpiresByType text/css "access plus 1 year"
447 |
448 | # Data interchange
449 | ExpiresByType application/json "access plus 0 seconds"
450 | ExpiresByType application/xml "access plus 0 seconds"
451 | ExpiresByType text/xml "access plus 0 seconds"
452 |
453 | # Favicon (cannot be renamed!)
454 | ExpiresByType image/x-icon "access plus 1 week"
455 |
456 | # HTML components (HTCs)
457 | ExpiresByType text/x-component "access plus 1 month"
458 |
459 | # HTML
460 | ExpiresByType text/html "access plus 0 seconds"
461 |
462 | # JavaScript
463 | ExpiresByType application/javascript "access plus 1 year"
464 |
465 | # Manifest files
466 | ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds"
467 | ExpiresByType text/cache-manifest "access plus 0 seconds"
468 |
469 | # Media
470 | ExpiresByType audio/ogg "access plus 1 month"
471 | ExpiresByType image/gif "access plus 1 month"
472 | ExpiresByType image/jpeg "access plus 1 month"
473 | ExpiresByType image/png "access plus 1 month"
474 | ExpiresByType video/mp4 "access plus 1 month"
475 | ExpiresByType video/ogg "access plus 1 month"
476 | ExpiresByType video/webm "access plus 1 month"
477 |
478 | # Web feeds
479 | ExpiresByType application/atom+xml "access plus 1 hour"
480 | ExpiresByType application/rss+xml "access plus 1 hour"
481 |
482 | # Web fonts
483 | ExpiresByType application/font-woff "access plus 1 month"
484 | ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
485 | ExpiresByType application/x-font-ttf "access plus 1 month"
486 | ExpiresByType font/opentype "access plus 1 month"
487 | ExpiresByType image/svg+xml "access plus 1 month"
488 |
489 |
490 |
491 | # ------------------------------------------------------------------------------
492 | # | Filename-based cache busting |
493 | # ------------------------------------------------------------------------------
494 |
495 | # If you're not using a build process to manage your filename version revving,
496 | # you might want to consider enabling the following directives to route all
497 | # requests such as `/css/style.12345.css` to `/css/style.css`.
498 |
499 | # To understand why this is important and a better idea than `*.css?v231`, read:
500 | # http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring
501 |
502 | #
503 | # RewriteCond %{REQUEST_FILENAME} !-f
504 | # RewriteCond %{REQUEST_FILENAME} !-d
505 | # RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L]
506 | #
507 |
508 | # ------------------------------------------------------------------------------
509 | # | File concatenation |
510 | # ------------------------------------------------------------------------------
511 |
512 | # Allow concatenation from within specific CSS and JS files, e.g.:
513 | # Inside of `script.combined.js` you could have
514 | #
515 | #
516 | # and they would be included into this single file.
517 |
518 | #
519 | #
520 | # Options +Includes
521 | # AddOutputFilterByType INCLUDES application/javascript application/json
522 | # SetOutputFilter INCLUDES
523 | #
524 | #
525 | # Options +Includes
526 | # AddOutputFilterByType INCLUDES text/css
527 | # SetOutputFilter INCLUDES
528 | #
529 | #
530 |
531 | # ------------------------------------------------------------------------------
532 | # | Persistent connections |
533 | # ------------------------------------------------------------------------------
534 |
535 | # Allow multiple requests to be sent over the same TCP connection:
536 | # http://httpd.apache.org/docs/current/en/mod/core.html#keepalive.
537 |
538 | # Enable if you serve a lot of static content but, be aware of the
539 | # possible disadvantages!
540 |
541 | #
542 | # Header set Connection Keep-Alive
543 | #
544 |
--------------------------------------------------------------------------------