Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support browser screen dimensions parameters #18

Closed
wants to merge 13 commits into from
40 changes: 37 additions & 3 deletions lib/snapshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ async function init_browser() {
return browser;
}

function take_snapshot( p_uri, p_filename, p_width, p_height ) {
function take_snapshot( p_uri, p_filename, p_width, p_height, screen_width, screen_height ) {
let m_uri = p_uri;
if ( ! m_uri.match( /^([a-z][a-z0-9+\-.]*):/i ) ) {
m_uri = 'http://' + m_uri;
Expand Down Expand Up @@ -184,7 +184,41 @@ function take_snapshot( p_uri, p_filename, p_width, p_height ) {
if ( response && response.ok() || page_loaded ) {
await page.waitFor( 2000 );
makeDirIfRequired( _path.dirname( p_filename ) );
await page.screenshot( { path: p_filename, fullPage: false, type: 'jpeg', quality: 90 } );

// Backwards compatibility requires we continue to allow
// screenshots longer than the target.
//
// However, as we support longer pages guessing the wrong
// length becomes uglier and more wasteful, so we'd like
// to limit the clip length to the screen size.
//
// Prior to an actual `/mshots/v2/` bump we can compromise by
// limiting to the document height only on requests that have
// specified the new screen_width/screen_height parameter.
let documentWidth = Number.MAX_SAFE_INTEGER;
let documentHeight = Number.MAX_SAFE_INTEGER;
if ( screen_width !== p_width || screen_height !== p_height ) {
var documentSize = await page.evaluate(
() => ( { documentWidth: document.documentElement.scrollWidth, documentHeight: document.documentElement.scrollHeight } ) );
documentHeight = documentSize.documentHeight;
documentWidth = documentSize.documentWidth;
}

const snapshotHeight = screen_height
? Math.min( screen_height, documentHeight )
: p_height;

const snapshotWidth = screen_width
? Math.min( screen_width, documentWidth )
: p_width;

await page.screenshot( { path: p_filename, fullPage: false, type: 'jpeg', quality: 90, clip: {
x: 0,
y: 0,
width: snapshotWidth,
height: snapshotHeight
} } );

await page.close();
logger.debug( process.pid + ': snapped: ' + p_uri );

Expand Down Expand Up @@ -349,7 +383,7 @@ function check_site_queue() {
var s_details = site_queue[0];
site_queue.shift();
logger.debug( process.pid + ': snapping: ' + s_details.url );
take_snapshot( s_details.url, s_details.file, s_details.width, s_details.height );
take_snapshot( s_details.url, s_details.file, s_details.width, s_details.height, s_details.screenWidth, s_details.screenHeight );
}
}
}
Expand Down
45 changes: 31 additions & 14 deletions lib/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,18 @@ const EXIT_MAXRAMUSAGE = 1;
const EXIT_COMMANDED = 2;
const EXIT_ERROR = 3;

const VIEWPORT_MAX_W = 1600;
const VIEWPORT_MAX_H = 1200;
// 3072px is retina display width
const VIEWPORT_MAX_W = 3072;
const VIEWPORT_MAX_H = 1920;
const VIEWPORT_MIN_W = 320;
const VIEWPORT_MIN_H = 320;

// 3072px is retina display width
const SCREEN_MAX_W = 3072;
const SCREEN_MAX_H = 7680;
const SCREEN_MIN_W = 320;
const SCREEN_MIN_H = 320;

const VIEWPORT_DEFAULT_W = 1280;
const VIEWPORT_DEFAULT_H = 960;

Expand Down Expand Up @@ -88,7 +96,7 @@ var HTTPListner = function() {
'/queue' : function( request, response ) {
response.writeHead( 200, { 'Content-Type': 'text/plain' } );
var _get = _url.parse( request.url, true ).query;
if ( ( undefined != _get['url'] ) && ( undefined != _get['f'] ) && ( "" != _get['url'] ) && ( "" != _get['f'] ) ) {
if ( _get['url'] && _get['f'] ) {
if ( ! readyToSendRequests ) {
response.write( 'Slap\n' );
response.end();
Expand All @@ -102,25 +110,34 @@ var HTTPListner = function() {
site.url = _get['url'];
site.file =_get['f'];

if ( undefined == _get['vpw'] || isNaN( _get['vpw'] ) ) {

if ( !_get['vpw'] || isNaN( _get['vpw'] ) ) {
site.width = VIEWPORT_DEFAULT_W;
} else {
site.width = parseInt( _get['vpw'] );
if ( site.width > VIEWPORT_MAX_W ) {
site.width = VIEWPORT_MAX_W;
} else if ( site.width < VIEWPORT_MIN_W ) {
site.width = VIEWPORT_MIN_W;
}
// bound width by min and max
site.width = Math.max(VIEWPORT_MIN_W, Math.min(site.width, VIEWPORT_MAX_W))
}
if ( undefined == _get['vph'] || isNaN( _get['vph'] ) ) {
site.height = VIEWPORT_DEFAULT_H;
} else {
site.height = parseInt( _get['vph'] );
if ( site.height > VIEWPORT_MAX_H ) {
site.height = VIEWPORT_MAX_H;
} else if ( site.height < VIEWPORT_MIN_H ) {
site.height = VIEWPORT_MIN_H;
}
// bound height by min and max
site.height = Math.max(VIEWPORT_MIN_H, Math.min(site.height, VIEWPORT_MAX_H))
}

if( !_get['screen_width']) {
site.screenWidth = site.width;
} else {
// bound image width by min and max
site.screenWidth = Math.max(SCREEN_MIN_W, Math.min(_get['screen_width'], SCREEN_MAX_W))
}

if( !_get['screen_height']) {
site.screenHeight = site.height;
} else {
// bound image height by min and max
site.screenHeight = Math.max(SCREEN_MIN_H, Math.min(_get['screen_height'], SCREEN_MAX_H))
}

if ( ! validateFileName( site ) ) {
Expand Down
31 changes: 29 additions & 2 deletions public_html/class-mshots.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class mShots {
const VIEWPORT_MIN_H = 320;
const VIEWPORT_DEFAULT_W = 1280;
const VIEWPORT_DEFAULT_H = 960;
const SCREEN_MAX_W = 1600;
const SCREEN_MAX_H = 3600;

protected $snapshot_url = "";
protected $snapshot_file = "";
Expand Down Expand Up @@ -84,6 +86,26 @@ function __construct() {
$this->viewport_h = self::VIEWPORT_MIN_H;
}

if ( isset( $_GET[ 'screen_width' ] ) ) {
$this->screen_width = intval( $_GET[ 'screen_width' ] );
if ( $this->screen_width > self::SCREEN_MAX_W ) {
$this->screen_width = self::SCREEN_MAX_W;
}
} else {
// default to viewport width
$this->screen_width = $this->viewport_w;
}

if ( isset( $_GET[ 'screen_height' ] ) ) {
$this->screen_height = intval( $_GET[ 'screen_height' ] );
if ( $this->screen_height > self::SCREEN_MAX_H ) {
$this->screen_height = self::SCREEN_MAX_H;
}
} else {
// default to viewport height
$this->screen_height = $this->viewport_h;
}

$this->snapshot_file = $this->resolve_filename( $this->snapshot_url );
}

Expand All @@ -105,6 +127,11 @@ public function requeue_snapshot() {
memcache_set( $m, $urlkey, 1, 0, 300 );

$requeue_url = self::renderer . "/queue?url=" . rawurlencode( $this->snapshot_url ) . "&f=" . urlencode( $this->snapshot_file );

if( $this->screen_width != $this->viewport_w || $this->screen_height != $this->viewport_h ) {
$requeue_url .= '&screen_width=' . $this->screen_width . '&screen_height=' . $this->screen_height;
}

if ( $this->viewport_w != self::VIEWPORT_DEFAULT_W || $this->viewport_h != self::VIEWPORT_DEFAULT_H )
$requeue_url .= '&vpw=' . $this->viewport_w . '&vph=' . $this->viewport_h;

Expand Down Expand Up @@ -193,11 +220,11 @@ private function image_resize_and_output( $image_filename ) {
$original_aspect = $width / $height;
// if we are not supplied with the width, use the original image's width
$thumb_width = ( isset( $_GET[ 'w' ] ) && $_GET[ 'w' ] ) ? $_GET[ 'w' ] : $width;
if ( $thumb_width > 1280 ) $thumb_width = 1280;
if ( $thumb_width > self::SCREEN_MAX_W ) $thumb_width = self::SCREEN_MAX_W;
if ( $thumb_width < 20 ) $thumb_width = 20;
// if we are not supplied with the height, calculate it from the original image aspect ratio
$thumb_height = ( isset( $_GET[ 'h' ] ) && $_GET[ 'h' ] ) ? $_GET[ 'h' ] : ( $thumb_width / ( $width / $height ) );
if ( $thumb_height > 960 ) $thumb_height = 960;
if ( $thumb_height > self::SCREEN_MAX_H ) $thumb_height = self::SCREEN_MAX_H;
if ( $thumb_height < 20 ) $thumb_height = 20;
$thumb_aspect = $thumb_width / $thumb_height;
if ( ( $thumb_width == $width && $thumb_height == $height ) ) {
Expand Down
77 changes: 48 additions & 29 deletions tests/snapshot.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,31 +31,40 @@ let tempFile;
*/
let staticServer;

/**
* A delay helper function
* @param {Number} delay
*/
function sleep(delay) {
return new Promise((r) => setTimeout(r, delay));
}

beforeAll(async () => {
staticServer = app.listen(3000);
browser = await snapshot.init_browser();
});

afterAll(async () => {
staticServer.close();
browser.close();
// wait for the browser to exit
await sleep(2000);
await browser.close();
});

// create a tmp file before each test
beforeEach(() => (tempFile = tmp.fileSync().name));
let mockSend;

beforeEach(() => {
// create a tmp file before each test
tempFile = tmp.fileSync().name;
// non-invasively receive snapshot queue notifications
mockSend = jest.spyOn(process, 'send');
});

// delete tmp file after every test
afterEach(() => fs.unlinkSync(tempFile));
afterEach(
() => {
// delete tmp file after every test
fs.unlinkSync(tempFile)
// restore real process.send
mockSend.mockRestore();
})

// Helper function: Queue a snapshot and wait for it to complete
async function getSnapshotResult( site ) {
return new Promise( ( resolve ) => {
mockSend.mockImplementationOnce( resolve )
snapshot.add_to_queue(site);
});
}

test("add_to_queue: should create a snapshot", async () => {
let site = {
Expand All @@ -65,11 +74,8 @@ test("add_to_queue: should create a snapshot", async () => {
height: 720,
};

snapshot.add_to_queue(site);

// snapshot polls the queue every 1000ms and screenshots take some time
await sleep(4000);

const result = await getSnapshotResult( site );
expect( result.payload.status ).toEqual( 0 );
expect(fs.existsSync(tempFile));
expect(fs.statSync(tempFile).size).toBeGreaterThan(0);
});
Expand All @@ -82,11 +88,7 @@ test("add_to_queue: should respect width and height params for normal height sit
height: 720,
};

snapshot.add_to_queue(site);

// snapshot polls the queue every 1000ms and screenshots take some time
await sleep(4000);

await getSnapshotResult( site );
const image = sharp(tempFile);
const metadata = await image.metadata();

Expand All @@ -102,14 +104,31 @@ test("add_to_queue: should respect width and height params for long sites", asyn
height: 720,
};

snapshot.add_to_queue(site);

// snapshot polls the queue every 1000ms and screenshots take some time
await sleep(4000);
await getSnapshotResult( site );

const image = sharp(tempFile);
const metadata = await image.metadata();

expect(metadata.width).toEqual(1280);
expect(metadata.height).toEqual(720);
});

test("add_to_queue: image dimensions should be equal to screenHeight and screenWidth for long enough sites", async () => {
let site = {
url: "http://127.0.0.1:3000/long-site.html",
file: tempFile,
width: 1280,
height: 720,
screenWidth: 1280,
screenHeight: 2048,
};

await getSnapshotResult( site );

const image = sharp(tempFile);
const metadata = await image.metadata();

expect(metadata.width).toEqual(site.screenWidth);
expect(metadata.height).toEqual(site.screenHeight);
// use 15000ms timeout because longer screenshots need more time
}, 15000);