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

Make row available to render_cell plugin hook #1300

Closed
abdusco opened this issue Apr 18, 2021 · 5 comments
Closed

Make row available to render_cell plugin hook #1300

abdusco opened this issue Apr 18, 2021 · 5 comments
Labels

Comments

@abdusco
Copy link
Contributor

abdusco commented Apr 18, 2021

Original title: Generating URL for a row inside render_cell hook

Hey,
I am using Datasette to view a database that contains video metadata. It has BLOB columns that contain video thumbnails in JPG format (around 100-500KB per row).

I've registered an output formatter that extends datasette.blob_renderer.render_blob function and serves the column with image/jpeg content type.

from datasette.blob_renderer import render_blob

async def render_jpg(datasette, database, rows, columns, request, table, view_name):
    response = await render_blob(datasette, database, rows, columns, request, table, view_name)
    response.content_type = "image/jpeg"
    response.headers["Content-Disposition"] = f'inline; filename="image.jpg"'
    return response


@hookimpl
def register_output_renderer():
    return {
        "extension": "jpg",
        "render": render_jpg,
        "can_render": lambda: True,
    }

This works well. I can visit http://localhost:8001/mydb/videos/1.jpg?_blob_column=thumbnail and view the image.

I want to display the image directly with an <img> tag (lazy-loaded of course). So, I need a URL, because embedding base64 would increase the page size too much (each image > 100KB).

Datasette generates a link with .blob extension for blob columns. It does this by calling datasette.urls.row_blob

display_value = jinja2.Markup(
'<a class="blob-download" href="{}">&lt;Binary:&nbsp;{}&nbsp;byte{}&gt;</a>'.format(
self.ds.urls.row_blob(
database,
table,
path_from_row_pks(row, pks, not pks),
column,
),
len(value),
"" if len(value) == 1 else "s",
)

But I have no way of getting the row inside the render_cell hook.

@hookimpl
def render_cell(value, column, table, database, datasette):
    if isinstance(value, bytes) and imghdr.what(None, value):
        # generate url
        return '$renderedLink'

Any pointers?

@abdusco
Copy link
Contributor Author

abdusco commented Apr 18, 2021

If I change the hookspec and add a row parameter, it works

def render_cell(value, column, table, database, datasette):

def render_cell(value, column, row, table, database, datasette):

But to generate a URL, I need the primary keys, but I can't call pks = await db.primary_keys(table) inside a sync function. I can't call datasette.utils.detect_primary_keys either, because the db connection is not publicly exposed (AFAICT).

@abdusco
Copy link
Contributor Author

abdusco commented Apr 18, 2021

If there's a simpler way to generate a URL for a specific row, I'm all ears

@abdusco abdusco changed the title Getting current row inside render_cell hook Generating URL for a row inside render_cell hook Apr 19, 2021
@abdusco
Copy link
Contributor Author

abdusco commented May 6, 2021

I ended up using some JS as a workaround.

First, add a JS file in metadata.yaml:

extra_js_urls:
  - '/static/app.js'

then inside the script, find the blob download links and replace .blob extension in the url with .jpg and replace the links with <img/> elements.
You need to add an output formatter to serve BLOB columns as JPG. You can find the code in the first post.
Replacing .blob -> .jpg might not even be necessary, because browsers only care about the mime type, so you only need to serve the binary content with the right content-type header.. You need to replace the extension, otherwise the output renderer will not run.

window.addEventListener('DOMContentLoaded', () => {
    function renderBlobImages() {
        document.querySelectorAll('a[href*=".blob"]').forEach(el => {
            const img = document.createElement('img');
            img.className = 'blob-image';
            img.loading = 'lazy';
            img.src = el.href.replace('.blob', '.jpg');
            el.parentElement.replaceChild(img, el);
        });
    }

    renderBlobImages();
});

while this does the job, I'd prefer handling this in Python where it belongs.

@simonw
Copy link
Owner

simonw commented Jul 7, 2022

Adding row to this plugin hook is a smart idea.

@simonw simonw changed the title Generating URL for a row inside render_cell hook Make row available to render_cell plugin hook Jul 7, 2022
@simonw simonw closed this as completed in 6373bb3 Jul 7, 2022
@simonw
Copy link
Owner

simonw commented Jul 7, 2022

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants