on master help sign in
Integrated media in results + Deezer Engine

New "embedded" item for the results, allow to give an iframe to display the media directly in the results. Note that the attributes src of the iframes are not set, but instead data-src is set, allowing to only load the iframe when clicked. Deezer engine based on public API (no key).

commit4a195e0b28fdd940e046c442032c816095416fec authorCqoicebordel <Cqoicebordel@users.noreply.github.com> dateMon 5 Jan 2015, 02:04 (12 years ago) parent7b531c6
10 files changed+147−12
searx/engines/dailymotion.py+11−4 searx/engines/deezer.py+62 searx/engines/soundcloud.py+12−2 searx/engines/vimeo.py+9−2 searx/engines/youtube.py+11−2 searx/settings.yml+4 searx/static/themes/oscar/js/searx.min.js+2−2 searx/static/themes/oscar/js/searx_src/element_modifiers.js+12 searx/templates/oscar/result_templates/default.html+12 searx/templates/oscar/result_templates/videos.html+12
searx/engines/dailymotion.py+11−4
@@ -6,12 +6,14 @@
66# @using-api yes
77# @results JSON
88# @stable yes
9−# @parse url, title, thumbnail
9+# @parse url, title, thumbnail, publishedDate, embedded
1010#
1111# @todo set content-parameter with correct data
1212
1313from urllib import urlencode
1414from json import loads
15+from cgi import escape
16+from datetime import datetime
1517
1618# engine dependent config
1719categories = ['videos']
@@ -20,7 +22,9 @@ language_support = True
2022
2123# search-url
2224# see http://www.dailymotion.com/doc/api/obj-video.html
23−search_url = 'https://api.dailymotion.com/videos?fields=title,description,duration,url,thumbnail_360_url&sort=relevance&limit=5&page={pageno}&{query}' # noqa
25+search_url = 'https://api.dailymotion.com/videos?fields=created_time,title,description,duration,url,thumbnail_360_url,id&sort=relevance&limit=5&page={pageno}&{query}' # noqa
26+embedded_url = '<iframe frameborder="0" width="540" height="304" ' +\
27+ 'data-src="//www.dailymotion.com/embed/video/{videoid}" allowfullscreen></iframe>'
2428
2529
2630# do search-request
@@ -51,14 +55,17 @@ def response(resp):
5155 for res in search_res['list']:
5256 title = res['title']
5357 url = res['url']
54− #content = res['description']
55− content = ''
58+ content = escape(res['description'])
5659 thumbnail = res['thumbnail_360_url']
60+ publishedDate = datetime.fromtimestamp(res['created_time'], None)
61+ embedded = embedded_url.format(videoid=res['id'])
5762
5863 results.append({'template': 'videos.html',
5964 'url': url,
6065 'title': title,
6166 'content': content,
67+ 'publishedDate': publishedDate,
68+ 'embedded': embedded,
6269 'thumbnail': thumbnail})
6370
6471 # return results
searx/engines/deezer.pynew file+62
@@ -0,0 +1,62 @@
1+## Deezer (Music)
2+#
3+# @website https://deezer.com
4+# @provide-api yes (http://developers.deezer.com/api/)
5+#
6+# @using-api yes
7+# @results JSON
8+# @stable yes
9+# @parse url, title, content, embedded
10+
11+from json import loads
12+from urllib import urlencode
13+
14+# engine dependent config
15+categories = ['music']
16+paging = True
17+
18+# search-url
19+url = 'http://api.deezer.com/'
20+search_url = url + 'search?{query}&index={offset}'
21+
22+embedded_url = '<iframe scrolling="no" frameborder="0" allowTransparency="true" ' +\
23+ 'data-src="http://www.deezer.com/plugins/player?type=tracks&id={audioid}" ' +\
24+ 'width="540" height="80"></iframe>'
25+
26+
27+# do search-request
28+def request(query, params):
29+ offset = (params['pageno'] - 1) * 25
30+
31+ params['url'] = search_url.format(query=urlencode({'q': query}),
32+ offset=offset)
33+
34+ return params
35+
36+
37+# get response from search-request
38+def response(resp):
39+ results = []
40+
41+ search_res = loads(resp.text)
42+
43+ # parse results
44+ for result in search_res.get('data', []):
45+ if result['type'] == 'track':
46+ print result
47+ title = result['title']
48+ url = result['link']
49+ content = result['artist']['name'] +\
50+ " &bull; " +\
51+ result['album']['title'] +\
52+ " &bull; " + result['title']
53+ embedded = embedded_url.format(audioid=result['id'])
54+
55+ # append result
56+ results.append({'url': url,
57+ 'title': title,
58+ 'embedded': embedded,
59+ 'content': content})
60+
61+ # return results
62+ return results
searx/engines/soundcloud.py+12−2
@@ -6,10 +6,11 @@
66# @using-api yes
77# @results JSON
88# @stable yes
9−# @parse url, title, content
9+# @parse url, title, content, publishedDate, embedded
1010
1111from json import loads
12−from urllib import urlencode
12+from urllib import urlencode, quote_plus
13+from dateutil import parser
1314
1415# engine dependent config
1516categories = ['music']
@@ -27,6 +28,10 @@ search_url = url + 'search?{query}'\
2728 '&linked_partitioning=1'\
2829 '&client_id={client_id}' # noqa
2930
31+embedded_url = '<iframe width="100%" height="166" ' +\
32+ 'scrolling="no" frameborder="no" ' +\
33+ 'data-src="https://w.soundcloud.com/player/?url={uri}"></iframe>'
34+
3035
3136# do search-request
3237def request(query, params):
@@ -50,10 +55,15 @@ def response(resp):
5055 if result['kind'] in ('track', 'playlist'):
5156 title = result['title']
5257 content = result['description']
58+ publishedDate = parser.parse(result['last_modified'])
59+ uri = quote_plus(result['uri'])
60+ embedded = embedded_url.format(uri=uri)
5361
5462 # append result
5563 results.append({'url': result['permalink_url'],
5664 'title': title,
65+ 'publishedDate': publishedDate,
66+ 'embedded': embedded,
5767 'content': content})
5868
5969 # return results
searx/engines/vimeo.py+9−2
@@ -7,7 +7,7 @@
77# @using-api no (TODO, rewrite to api)
88# @results HTML (using search portal)
99# @stable no (HTML can change)
10−# @parse url, title, publishedDate, thumbnail
10+# @parse url, title, publishedDate, thumbnail, embedded
1111#
1212# @todo rewrite to api
1313# @todo set content-parameter with correct data
@@ -33,6 +33,10 @@ title_xpath = './a/div[@class="data"]/p[@class="title"]/text()'
3333results_xpath = '//div[@id="browse_content"]/ol/li'
3434publishedDate_xpath = './/p[@class="meta"]//attribute::datetime'
3535
36+embedded_url = '<iframe data-src="//player.vimeo.com/video{videoid}" ' +\
37+ 'width="540" height="304" frameborder="0" ' +\
38+ 'webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>'
39+
3640
3741# do search-request
3842def request(query, params):
@@ -56,11 +60,13 @@ def response(resp):
5660
5761 # parse results
5862 for result in dom.xpath(results_xpath):
59− url = base_url + result.xpath(url_xpath)[0]
63+ videoid = result.xpath(url_xpath)[0]
64+ url = base_url + videoid
6065 title = p.unescape(extract_text(result.xpath(title_xpath)))
6166 thumbnail = extract_text(result.xpath(content_xpath)[0])
6267 publishedDate = parser.parse(extract_text(
6368 result.xpath(publishedDate_xpath)[0]))
69+ embedded = embedded_url.format(videoid=videoid)
6470
6571 # append result
6672 results.append({'url': url,
@@ -68,6 +74,7 @@ def response(resp):
6874 'content': '',
6975 'template': 'videos.html',
7076 'publishedDate': publishedDate,
77+ 'embedded': embedded,
7178 'thumbnail': thumbnail})
7279
7380 # return results
searx/engines/youtube.py+11−2
@@ -6,7 +6,7 @@
66# @using-api yes
77# @results JSON
88# @stable yes
9−# @parse url, title, content, publishedDate, thumbnail
9+# @parse url, title, content, publishedDate, thumbnail, embedded
1010
1111from json import loads
1212from urllib import urlencode
@@ -19,7 +19,11 @@ language_support = True
1919
2020# search-url
2121base_url = 'https://gdata.youtube.com/feeds/api/videos'
22−search_url = base_url + '?alt=json&{query}&start-index={index}&max-results=5' # noqa
22+search_url = base_url + '?alt=json&{query}&start-index={index}&max-results=5'
23+
24+embedded_url = '<iframe width="540" height="304" ' +\
25+ 'data-src="//www.youtube-nocookie.com/embed/{videoid}" ' +\
26+ 'frameborder="0" allowfullscreen></iframe>'
2327
2428
2529# do search-request
@@ -60,6 +64,8 @@ def response(resp):
6064 if url.endswith('&'):
6165 url = url[:-1]
6266
67+ videoid = url[32:]
68+
6369 title = result['title']['$t']
6470 content = ''
6571 thumbnail = ''
@@ -72,12 +78,15 @@ def response(resp):
7278
7379 content = result['content']['$t']
7480
81+ embedded = embedded_url.format(videoid=videoid)
82+
7583 # append result
7684 results.append({'url': url,
7785 'title': title,
7886 'content': content,
7987 'template': 'videos.html',
8088 'publishedDate': publishedDate,
89+ 'embedded': embedded,
8190 'thumbnail': thumbnail})
8291
8392 # return results
searx/settings.yml+4
@@ -35,6 +35,10 @@ engines:
3535 engine : currency_convert
3636 categories : general
3737 shortcut : cc
38+
39+ - name : deezer
40+ engine : deezer
41+ shortcut : dz
3842
3943 - name : deviantart
4044 engine : deviantart
searx/static/themes/oscar/js/searx.min.js+2−2
@@ -1,2 +1,2 @@
1−/*! oscar/searx.min.js | 19-12-2014 | https://github.com/asciimoo/searx */
2−requirejs.config({baseUrl:"./static/themes/oscar/js",paths:{app:"../app"}}),searx.autocompleter&&(searx.searchResults=new Bloodhound({datumTokenizer:Bloodhound.tokenizers.obj.whitespace("value"),queryTokenizer:Bloodhound.tokenizers.whitespace,remote:"/autocompleter?q=%QUERY"}),searx.searchResults.initialize()),$(document).ready(function(){searx.autocompleter&&$("#q").typeahead(null,{name:"search-results",displayKey:function(a){return a},source:searx.searchResults.ttAdapter()})}),$(document).ready(function(){$("#q.autofocus").focus(),$(".select-all-on-click").click(function(){$(this).select()}),$(".btn-collapse").click(function(){var a=$(this).data("btn-text-collapsed"),b=$(this).data("btn-text-not-collapsed");""!==a&&""!==b&&(new_html=$(this).hasClass("collapsed")?$(this).html().replace(a,b):$(this).html().replace(b,a),$(this).html(new_html))}),$(".btn-toggle .btn").click(function(){var a="btn-"+$(this).data("btn-class"),b=$(this).data("btn-label-default"),c=$(this).data("btn-label-toggled");""!==c&&(new_html=$(this).hasClass("btn-default")?$(this).html().replace(b,c):$(this).html().replace(c,b),$(this).html(new_html)),$(this).toggleClass(a),$(this).toggleClass("btn-default")}),$(".btn-sm").dblclick(function(){var a="btn-"+$(this).data("btn-class");$(this).hasClass("btn-default")?($(".btn-sm > input").attr("checked","checked"),$(".btn-sm > input").prop("checked",!0),$(".btn-sm").addClass(a),$(".btn-sm").addClass("active"),$(".btn-sm").removeClass("btn-default")):($(".btn-sm > input").attr("checked",""),$(".btn-sm > input").removeAttr("checked"),$(".btn-sm > input").checked=!1,$(".btn-sm").removeClass(a),$(".btn-sm").removeClass("active"),$(".btn-sm").addClass("btn-default"))})}),$(document).ready(function(){$(".searx_overpass_request").on("click",function(a){var b="https://overpass-api.de/api/interpreter?data=",c=b+"[out:json][timeout:25];(",d=");out meta;",e=$(this).data("osm-id"),f=$(this).data("osm-type"),g=$(this).data("result-table"),h="#"+$(this).data("result-table-loadicon"),i=["addr:city","addr:country","addr:housenumber","addr:postcode","addr:street"];if(e&&f&&g){g="#"+g;var j=null;switch(f){case"node":j=c+"node("+e+");"+d;break;case"way":j=c+"way("+e+");"+d;break;case"relation":j=c+"relation("+e+");"+d}if(j){$.ajax(j).done(function(a){if(a&&a.elements&&a.elements[0]){var b=a.elements[0],c=$(g).html();for(var d in b.tags)if(null===b.tags.name||-1==i.indexOf(d)){switch(c+="<tr><td>"+d+"</td><td>",d){case"phone":case"fax":c+='<a href="tel:'+b.tags[d].replace(/ /g,"")+'">'+b.tags[d]+"</a>";break;case"email":c+='<a href="mailto:'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"website":case"url":c+='<a href="'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"wikidata":c+='<a href="https://www.wikidata.org/wiki/'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"wikipedia":if(-1!=b.tags[d].indexOf(":")){c+='<a href="https://'+b.tags[d].substring(0,b.tags[d].indexOf(":"))+".wikipedia.org/wiki/"+b.tags[d].substring(b.tags[d].indexOf(":")+1)+'">'+b.tags[d]+"</a>";break}default:c+=b.tags[d]}c+="</td></tr>"}$(g).html(c),$(g).removeClass("hidden"),$(h).addClass("hidden")}}).fail(function(){$(h).html($(h).html()+'<p class="text-muted">could not load data!</p>')})}}$(this).off(a)}),$(".searx_init_map").on("click",function(a){var b=$(this).data("leaflet-target"),c=$(this).data("map-lon"),d=$(this).data("map-lat"),e=$(this).data("map-zoom"),f=$(this).data("map-boundingbox"),g=$(this).data("map-geojson");require(["leaflet-0.7.3.min"],function(){f&&(southWest=L.latLng(f[0],f[2]),northEast=L.latLng(f[1],f[3]),map_bounds=L.latLngBounds(southWest,northEast)),L.Icon.Default.imagePath="./static/oscar/img/map";{var a=L.map(b),h="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",i='Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors',j=new L.TileLayer(h,{minZoom:1,maxZoom:19,attribution:i}),k="http://otile{s}.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.jpg",l='Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors | Tiles Courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> <img src="http://developer.mapquest.com/content/osm/mq_logo.png">',m=new L.TileLayer(k,{minZoom:1,maxZoom:18,subdomains:"1234",attribution:l}),n="http://otile{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg",o='Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors | Tiles Courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> <img src="https://developer.mapquest.com/content/osm/mq_logo.png"> | Portions Courtesy NASA/JPL-Caltech and U.S. Depart. of Agriculture, Farm Service Agency';new L.TileLayer(n,{minZoom:1,maxZoom:11,subdomains:"1234",attribution:o})}map_bounds?setTimeout(function(){a.fitBounds(map_bounds,{maxZoom:17})},0):c&&d&&(e?a.setView(new L.LatLng(d,c),e):a.setView(new L.LatLng(d,c),8)),a.addLayer(m);var p={"OSM Mapnik":j,MapQuest:m};L.control.layers(p).addTo(a),g&&L.geoJson(g).addTo(a)}),$(this).off(a)})});
1+/*! oscar/searx.min.js | 04-01-2015 | https://github.com/asciimoo/searx */
2+requirejs.config({baseUrl:"./static/themes/oscar/js",paths:{app:"../app"}}),searx.autocompleter&&(searx.searchResults=new Bloodhound({datumTokenizer:Bloodhound.tokenizers.obj.whitespace("value"),queryTokenizer:Bloodhound.tokenizers.whitespace,remote:"/autocompleter?q=%QUERY"}),searx.searchResults.initialize()),$(document).ready(function(){searx.autocompleter&&$("#q").typeahead(null,{name:"search-results",displayKey:function(a){return a},source:searx.searchResults.ttAdapter()})}),$(document).ready(function(){$("#q.autofocus").focus(),$(".select-all-on-click").click(function(){$(this).select()}),$(".btn-collapse").click(function(){var a=$(this).data("btn-text-collapsed"),b=$(this).data("btn-text-not-collapsed");""!==a&&""!==b&&(new_html=$(this).hasClass("collapsed")?$(this).html().replace(a,b):$(this).html().replace(b,a),$(this).html(new_html))}),$(".btn-toggle .btn").click(function(){var a="btn-"+$(this).data("btn-class"),b=$(this).data("btn-label-default"),c=$(this).data("btn-label-toggled");""!==c&&(new_html=$(this).hasClass("btn-default")?$(this).html().replace(b,c):$(this).html().replace(c,b),$(this).html(new_html)),$(this).toggleClass(a),$(this).toggleClass("btn-default")}),$(".media-loader").click(function(){var a=$(this).data("target"),b=$(a+" > iframe"),c=b.attr("src");(void 0===c||c===!1)&&b.attr("src",b.data("src"))}),$(".btn-sm").dblclick(function(){var a="btn-"+$(this).data("btn-class");$(this).hasClass("btn-default")?($(".btn-sm > input").attr("checked","checked"),$(".btn-sm > input").prop("checked",!0),$(".btn-sm").addClass(a),$(".btn-sm").addClass("active"),$(".btn-sm").removeClass("btn-default")):($(".btn-sm > input").attr("checked",""),$(".btn-sm > input").removeAttr("checked"),$(".btn-sm > input").checked=!1,$(".btn-sm").removeClass(a),$(".btn-sm").removeClass("active"),$(".btn-sm").addClass("btn-default"))})}),$(document).ready(function(){$(".searx_overpass_request").on("click",function(a){var b="https://overpass-api.de/api/interpreter?data=",c=b+"[out:json][timeout:25];(",d=");out meta;",e=$(this).data("osm-id"),f=$(this).data("osm-type"),g=$(this).data("result-table"),h="#"+$(this).data("result-table-loadicon"),i=["addr:city","addr:country","addr:housenumber","addr:postcode","addr:street"];if(e&&f&&g){g="#"+g;var j=null;switch(f){case"node":j=c+"node("+e+");"+d;break;case"way":j=c+"way("+e+");"+d;break;case"relation":j=c+"relation("+e+");"+d}if(j){$.ajax(j).done(function(a){if(a&&a.elements&&a.elements[0]){var b=a.elements[0],c=$(g).html();for(var d in b.tags)if(null===b.tags.name||-1==i.indexOf(d)){switch(c+="<tr><td>"+d+"</td><td>",d){case"phone":case"fax":c+='<a href="tel:'+b.tags[d].replace(/ /g,"")+'">'+b.tags[d]+"</a>";break;case"email":c+='<a href="mailto:'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"website":case"url":c+='<a href="'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"wikidata":c+='<a href="https://www.wikidata.org/wiki/'+b.tags[d]+'">'+b.tags[d]+"</a>";break;case"wikipedia":if(-1!=b.tags[d].indexOf(":")){c+='<a href="https://'+b.tags[d].substring(0,b.tags[d].indexOf(":"))+".wikipedia.org/wiki/"+b.tags[d].substring(b.tags[d].indexOf(":")+1)+'">'+b.tags[d]+"</a>";break}default:c+=b.tags[d]}c+="</td></tr>"}$(g).html(c),$(g).removeClass("hidden"),$(h).addClass("hidden")}}).fail(function(){$(h).html($(h).html()+'<p class="text-muted">could not load data!</p>')})}}$(this).off(a)}),$(".searx_init_map").on("click",function(a){var b=$(this).data("leaflet-target"),c=$(this).data("map-lon"),d=$(this).data("map-lat"),e=$(this).data("map-zoom"),f=$(this).data("map-boundingbox"),g=$(this).data("map-geojson");require(["leaflet-0.7.3.min"],function(){f&&(southWest=L.latLng(f[0],f[2]),northEast=L.latLng(f[1],f[3]),map_bounds=L.latLngBounds(southWest,northEast)),L.Icon.Default.imagePath="./static/themes/oscar/img/map";{var a=L.map(b),h="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",i='Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors',j=new L.TileLayer(h,{minZoom:1,maxZoom:19,attribution:i}),k="http://otile{s}.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.jpg",l='Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors | Tiles Courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> <img src="http://developer.mapquest.com/content/osm/mq_logo.png">',m=new L.TileLayer(k,{minZoom:1,maxZoom:18,subdomains:"1234",attribution:l}),n="http://otile{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg",o='Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors | Tiles Courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> <img src="https://developer.mapquest.com/content/osm/mq_logo.png"> | Portions Courtesy NASA/JPL-Caltech and U.S. Depart. of Agriculture, Farm Service Agency';new L.TileLayer(n,{minZoom:1,maxZoom:11,subdomains:"1234",attribution:o})}map_bounds?setTimeout(function(){a.fitBounds(map_bounds,{maxZoom:17})},0):c&&d&&(e?a.setView(new L.LatLng(d,c),e):a.setView(new L.LatLng(d,c),8)),a.addLayer(m);var p={"OSM Mapnik":j,MapQuest:m};L.control.layers(p).addTo(a),g&&L.geoJson(g).addTo(a)}),$(this).off(a)})});
searx/static/themes/oscar/js/searx_src/element_modifiers.js+12
@@ -63,6 +63,18 @@ $(document).ready(function(){
6363 $(this).toggleClass(btnClass);
6464 $(this).toggleClass('btn-default');
6565 });
66+
67+ /**
68+ * change text during btn-toggle click if possible
69+ */
70+ $('.media-loader').click(function() {
71+ var target = $(this).data('target');
72+ var iframe_load = $(target + ' > iframe');
73+ var srctest = iframe_load.attr('src');
74+ if(srctest === undefined || srctest === false){
75+ iframe_load.attr('src', iframe_load.data('src'));
76+ }
77+ });
6678
6779 /**
6880 * Select or deselect every categories on double clic
searx/templates/oscar/result_templates/default.html+12
@@ -5,8 +5,20 @@
55{% if result.publishedDate %}<time class="text-muted" datetime="{{ result.pubdate }}" >{{ result.publishedDate }}</time>{% endif %}
66<small><a class="text-info" href="https://web.archive.org/web/{{ result.url }}">{{ icon('link') }} {{ _('cached') }}</a></small>
77
8+{% if result.embedded %}
9+ <small> &bull; <a class="text-info btn-collapse collapsed cursor-pointer media-loader" data-toggle="collapse" data-target="#result-media-{{ index }}" data-btn-text-collapsed="{{ _('show media') }}" data-btn-text-not-collapsed="{{ _('hide media') }}">{{ icon('music') }} {{ _('show media') }}</a></small>
10+{% endif %}
11+
812{% if result.content %}<p class="result-content">{{ result.content|safe }}</p>{% endif %}
913
14+{% if result.embedded %}
15+<div id="result-media-{{ index }}" class="collapse">
16+{% autoescape false %}
17+ {{ result.embedded }}
18+{% endautoescape %}
19+</div>
20+{% endif %}
21+
1022<div class="clearfix"></div>
1123
1224<span class="label label-default pull-right">{{ result.engine }}</span>
searx/templates/oscar/result_templates/videos.html+12
@@ -5,6 +5,10 @@
55{% if result.publishedDate %}<time class="text-muted" datetime="{{ result.pubdate }}" >{{ result.publishedDate }}</time>{% endif %}
66<small><a class="text-info" href="https://web.archive.org/web/{{ result.url }}">{{ icon('link') }} {{ _('cached') }}</a></small>
77
8+{% if result.embedded %}
9+ <small> &bull; <a class="text-info btn-collapse collapsed cursor-pointer media-loader" data-toggle="collapse" data-target="#result-video-{{ index }}" data-btn-text-collapsed="{{ _('show video') }}" data-btn-text-not-collapsed="{{ _('hide video') }}">{{ icon('film') }} {{ _('show video') }}</a></small>
10+{% endif %}
11+
812<div class="container-fluid">
913 <div class="row">
1014 <a href="{{ result.url }}"><img class="thumbnail col-xs-6 col-sm-4 col-md-4 result-content" src="{{ result.thumbnail|safe }}" alt="{{ result.title|striptags }} {{ result.engine }}" /></a>
@@ -12,6 +16,14 @@
1216 </div>
1317</div>
1418
19+{% if result.embedded %}
20+<div id="result-video-{{ index }}" class="collapse">
21+{% autoescape false %}
22+ {{ result.embedded }}
23+{% endautoescape %}
24+</div>
25+{% endif %}
26+
1527<div class="clearfix"></div>
1628
1729<span class="label label-default pull-right">{{ result.engine }}</span>