Back

Screenshot Streamer

Post screeshots via API and have them neatly appear in all clients without reloading


This is a streaming app that pushes uploaded images to all connected clients. The server is built with Sinatra and sends push notifications to all connected clients once it receives a proper POST request.

To test the front-end code Jasmine and Grunt are used and the testsuite runs headless, started via grunt jasmine.

While the app works, it could use some more work: There are no tests for the Sinatra app and the jQuery plugin could use some refactoring as well.

Code snippets

get '/' do
@images = images
erb :index
end

post '/screenshot/:filename' do
protected!
write_file(params[:filename])

notification = params.merge( {'timestamp' => timestamp}).to_json

connections.each { |out| out << "data: #{notification}\n\n"}
"wrote to #{params[:filename]}\n"
end

get '/connect', provides: 'text/event-stream' do
stream :keep_open do |out|
connections << out
out.callback { connections.delete(out) }
end
end

The routes of the Sinatra app.


(function ( $ ) {
$.fn.moveImages = function(options) {
var settings = $.extend({
filename: "#",
limitThumbnails: 6
}, options );

img_copy = $('#newest_image').clone();
img_copy.removeAttr("id");

// only add thumbnail if newest image has a source
if($("#thumbnails ul li").size() == 0 && $('#newest_image').attr("href")) {
$("#thumbnails ul").prepend("<li></li>");
$("#thumbnails li").first().html(img_copy);
}
else if($("#thumbnails ul li").size() == 1) {
oldFirstLi = $("#thumbnails li").first();
newFirstLi = oldFirstLi.clone().prependTo(oldFirstLi.parent());
newFirstLi.attr("class", "first");
oldFirstLi.attr("class", "last");
newFirstLi.html(img_copy);
}
else {
oldFirstLi = $("#thumbnails ul li").first();
//Remove the class attr of the first element
oldFirstLi.removeAttr("class");

// Remove the last list element if the limit is reached
if($('#thumbnails ul li').size() >= settings.limitThumbnails) {
$("#thumbnails ul li").last().remove();
// Set the class of the new last element to "last"
$("#thumbnails li").last().attr("class", "last");
}

//Copy the big image to the thumbnails
newFirstLi = oldFirstLi.clone().prependTo(oldFirstLi.parent());
newFirstLi.attr("class", "first");
newFirstLi.html(img_copy);
}

$('#newest_image').attr("href", "/screenshots/" + settings.filename);
$('#newest_image img').attr("src", "/screenshots/" + settings.filename);
};
}( jQuery ));

The jQuery plugin to move around the images.


describe("adding the first image", function() {
var fixture;

beforeEach(function() {
loadFixtures('noImage.html');
fixture = $('#test');
fixture.moveImages({filename: "whatever.png"});
});

afterEach(function() {
fixture.remove();
});

it('should add the correct link to the empty a href', function() {
expect('a#newest_image').toHaveAttr('href', '/screenshots/whatever.png');
});

it('should add the correct link to the empty img src', function() {
expect('a#newest_image img').toHaveAttr('src', '/screenshots/whatever.png');
});

it('should add nothing to the thumbnail list', function() {
expect($('#thumbnails ul')).not.toContainElement('li');
});
});

Jasmine tests for the jQuery plugin.

Download

No download available.

Screenshot Streamer on GitHub/GitLab

Download the latest source or clone/fork the project from source control.