diff --git a/package.json b/package.json
index dc1fa88a605cb46008ce99052559beb50f436155..e9e8f0edb1cad820b03788406ec61e28ba84f305 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
   "description": "",
   "main": "index.js",
   "scripts": {
-    "test": "./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha -- sitegin/test/ --recursive -R spec -u exports -t 60000"
+    "test": "./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha -- sitegin/tests/ --recursive -R spec -u exports -t 60000"
   },
   "author": "",
   "license": "UNSPECIFIED",
diff --git a/sitegin/markdown.js b/sitegin/markdown.js
index df8dd7188b8cb6df82d11f5041a43becc3f09197..7a04c7588ddeb1e5ab59d59ba6384fa0bed8623c 100644
--- a/sitegin/markdown.js
+++ b/sitegin/markdown.js
@@ -8,6 +8,7 @@ var renderer = new marked.Renderer();
 var highlightjs = require('highlight.js');
 var path = require('path');
 var jobs = require('./jobs');
+var youtube = require('./utils/youtube.js');
 
 var toURL = function(url) {
   return jobs.run('toURL', url);
@@ -30,49 +31,68 @@ renderer.paragraph = function(text) {
   else return '<p>'+text+'</p>\n';
 }
 
-var renderImage = function(href, title, text, curFilename) {
-  var parseParams = function(params) {
-    for(let param of params.split(',')) {
-      param = param.trim();
-      // 123x or x123 or 123x123
-      if(param.match(/[0-9]*x[0-9]+/) || param.match(/[0-9]+x[0-9]*/)) {
-        options.size = param.split('x');
-        if(options.size !== undefined) {
-          if(options.size[0].length > 0) {
-            options.width = options.size[0];
-          }
-          if(options.size.length > 1 && options.size[1].length > 0) {
-            options.height = options.size[1];
-          }
-        }
-      } else if(param == 'nolightbox') {
-        options.nolightbox = true;
+var paramInHrefParser = function(href) {
+  var sepIndex = href.indexOf(' =');
+  var ret = {
+    href: href,
+    options: {}
+  }
+  if(sepIndex < 0) {
+    return ret;
+  } else {
+    ret.href = href.substr(0, sepIndex);
+    var options = href.substr(sepIndex+2);
+    options = options // split by ',' but not '\,'
+      .replace('a,','\u0007')
+      .split(',')
+      .map(function(e) {return e.replace('\u0007','a,')});
+    options.forEach(function(option) {
+      var index = option.indexOf('=');
+      if(index < 0) {
+        ret.options[option] = true;
+      } else {
+        var key = option.substr(0,index);
+        var value = option.substr(index+1);
+        ret.options[key] = value;
       }
-    }
+    });
+    return ret;
   }
+}
 
-  href = href.split(path.sep).join('/');
-  href = href.split(' =');
-  var params = href[1];
-  var options = {};
+var renderYoutube = function(href, options) {
+  var info = youtube.info(href);
+  let out = '<div class="video-container"><div class="videodiv">';
+  out += '<iframe src="https://www.youtube.com/embed/'+info.videoid+
+  '?modestbranding&start='+info.time+'" frameborder="0" allowfullscreen></iframe>'
+  out += '</div></div>'
 
-  if(params) {
-    parseParams(params);
-  }
-  href = href[0];
-  if(href.match('//(www.)?youtube.com/watch') || href.match('//youtu.be/')) {
-    var id;
-    if(href.match('//youtube.com/watch')) {
-      id = href.substr(href.indexOf('v=')+2).split('&')[0];
-    } else {
-      id = href.substr(href.indexOf('.be/')+4).split('?')[0];
-    }
+  return out;
+}
 
-    let out = '<div class="video-container"><div class="videodiv">';
-    out += '<iframe src="https://www.youtube.com/embed/'+id+'" frameborder="0" allowfullscreen></iframe>'
-    out += '</div></div>'
+var renderImage = function(href, title, text, curFilename) {
+  var parsed = paramInHrefParser(href);
+  href = parsed.href;
+  var options = parsed.options;
+
+  for(let option in options) {
+    if( !options.hasOwnProperty(option) ) continue;
+
+    if(option.match(/x[0-9]+/) || option.match(/[0-9]+x[0-9]*/)) { // 123x or x123 or 123x123
+      options.size = option.split('x');
+      if(options.size !== undefined) {
+        if(options.size[0].length > 0) {
+          options.width = options.size[0];
+        }
+        if(options.size.length > 1 && options.size[1].length > 0) {
+          options.height = options.size[1];
+        }
+      }
+    }
+  }
 
-    return out;
+  if(youtube.isVideo(href)) {
+    return renderYoutube(href, options);
   } else {
     var rel = path.relative(process.cwd(),path.resolve(curFilename,href));
 
@@ -147,3 +167,9 @@ module.exports = function(obj) {
   });
   return Promise.resolve(obj);
 }
+
+module.exports._test = {
+  renderImage: renderImage,
+  paramInHrefParser: paramInHrefParser,
+  renderYoutube: renderYoutube
+};
diff --git a/sitegin/tests/markdownTest.js b/sitegin/tests/markdownTest.js
new file mode 100644
index 0000000000000000000000000000000000000000..1590e4583485cb5fb08d232839664058e22e18e4
--- /dev/null
+++ b/sitegin/tests/markdownTest.js
@@ -0,0 +1,15 @@
+require('should');
+
+var o = require('../markdown')._test;
+
+describe('Markdown', function() {
+  it('paramInHrefParser image.png =option,option2=123', function() {
+    o.paramInHrefParser('image.png =option,option2=123').should.be.eql({
+      href: 'image.png',
+      options: {
+        option: true,
+        option2: '123'
+      }
+    })
+  })
+})
diff --git a/sitegin/tests/youtubeTest.js b/sitegin/tests/youtubeTest.js
new file mode 100644
index 0000000000000000000000000000000000000000..17f991428e61a7779958f3ff7d48439d81d132da
--- /dev/null
+++ b/sitegin/tests/youtubeTest.js
@@ -0,0 +1,50 @@
+'use strict';
+/*jshint expr: true*/
+require('should');
+
+var youtube = require('../utils/youtube.js');
+
+describe('Youtube', function() {
+  let links = {
+    'https://www.youtube.com/watch?v=QH2-TGUlwu4': true,
+    'http://www.youtube.com/watch?v=QH2-TGUlwu4': true,
+    'https://youtu.be/QH2-TGUlwu4': true,
+    'https://youtu.be/QH2-TGUlwu4?t=3m30s': true,
+    'https://www.youtube.com/watch?t=3m30s&v=QH2-TGUlwu4': true,
+    'https://www.youtube.cz/watch?t=3m30s&v=QH2-TGUlwu4': true,
+    'https://www.youtube.com/watch?app=desktop&gl=CZ&v=QH2-TGUlwu4&hl=cs&v=cTa1Q2mBeJE': true,
+    'https://www.youtube.com/': false,
+    'https://www.youtube.com/channel/UCzNxmbk_LA1jXvYgivCCkpw': false,
+    'youtube.png': false
+  }
+  function testLink(link, val) {
+    it('isVideo '+link+' => '+val,function() {
+      youtube.isVideo(link).should.be.equal(val);
+    })
+  }
+  for(let link in links) {
+    if(!links.hasOwnProperty(link)) continue;
+    testLink(link, links[link]);
+  }
+
+  it('info https://youtu.be/wZZ7oFKsKzY?t=3h30m30s', function() {
+    var info = youtube.info('https://youtu.be/wZZ7oFKsKzY?t=3h30m30s');
+    info.should.be.object;
+    info.time.should.be.eql(3*3600+30*60+30);
+    info.videoid.should.be.eql('wZZ7oFKsKzY');
+  })
+
+  it('info https://youtu.be/QH2-TGUlwu4', function() {
+    var info = youtube.info('https://youtu.be/QH2-TGUlwu4');
+    info.should.be.object;
+    info.time.should.be.eql(0);
+    info.videoid.should.be.eql('QH2-TGUlwu4');
+  })
+
+  it('info https://www.youtube.com/watch?t=3m30s&v=QH2-TGUlwu4', function() {
+    var info = youtube.info('https://www.youtube.com/watch?t=3m30s&v=QH2-TGUlwu4');
+    info.should.be.object;
+    info.time.should.be.eql(3*60+30);
+    info.videoid.should.be.eql('QH2-TGUlwu4');
+  })
+})
diff --git a/sitegin/toURL.js b/sitegin/toURL.js
index 997e3c0718518e8a037d631d1357de3dfc9de0ad..7d765bb6e886c4dfea799399a239f8dfc78d34a6 100644
--- a/sitegin/toURL.js
+++ b/sitegin/toURL.js
@@ -7,5 +7,5 @@ var latinise = function(l){return l.replace(/[^A-Za-z0-9\[\] ]/g,function(a){ret
 
 module.exports = function(tag) {
   if(tag === undefined) return undefined;
-  return latinise(tag.toLowerCase()).replace(/ /g,'-');
+  return latinise(tag.toLowerCase()).replace(/ /g,'-').replace(/-+/g,'-');
 }
diff --git a/sitegin/utils/youtube.js b/sitegin/utils/youtube.js
new file mode 100644
index 0000000000000000000000000000000000000000..46913cc29bb1b973d3993dabd2819176a7e05db2
--- /dev/null
+++ b/sitegin/utils/youtube.js
@@ -0,0 +1,57 @@
+'use strict';
+
+var url = require('url');
+var querystring = require('querystring');
+
+module.exports.isVideo = function(href) {
+  var o = url.parse(href);
+  if(!o.host) return false;
+
+  if(o.host == 'youtu.be') {
+    return o.pathname.length > 1;
+  }
+
+  // not youtube link
+  if(!o.host.match('youtube.[a-zA-Z]+$')) return false;
+
+  var query = querystring.parse(o.query);
+  // not youtube video
+  if(!query.v) return false;
+
+  return true;
+}
+
+module.exports.info = function(href) {
+  href = url.parse(href);
+  var query = querystring.parse(href.query);
+  var info = {
+    videoid: false,
+    time: 0
+  }
+
+  if(href.host == 'youtu.be') {
+    if(href.pathname.length < 2) throw new Error(href+' is not video link');
+    info.videoid = href.pathname.substr(1);
+  } else if(href.host.match('youtube.[a-zA-Z]+$')) {
+    if(Array.isArray(query.v))
+      info.videoid = query.v[0];
+    else
+      info.videoid = query.v;
+  } else {
+    throw new Error(href+' is not youtube url');
+  }
+  if(query.t) {
+    var units = query.t.split(/[0-9]+/);
+    units.splice(0,1);
+    var values = query.t.split(/[^0-9]/);
+    values.splice(-1,1);
+    if(units.length != values.length) throw new Error('Youtube: Wrong time specifier: '+query.t);
+    for(let i = 0; i < units.length; i++) {
+      if(units[i] == 'h') info.time += Number(values[i])*3600;
+      else if(units[i] == 'm') info.time += Number(values[i])*60;
+      else if(units[i] == 's') info.time += Number(values[i]);
+      else throw new Error('Youtube: Wrong time specifier: '+query.t);
+    }
+  }
+  return info;
+}