Package: npf-renderer
npf-rendererA Python library for rendering Tumblr's Neue Post Format (NPF) into HTMLOutputUsage:importpytumblrimportnpf_rendererclient=pytumblr.TumblrRestClient(*TUMBLR_API_KEYS)blog=client.posts(BLOG_NAME,npf=True)post=blog["posts"][0]content,layout=post["content"],post["layout"]has_error,post_html=npf_renderer.format_npf(content,layout,pretty_html=True)print(post_html)HTML<divclass="post-body"><divclass="layout-row"><pclass="text-block"><spanclass="inline-formatted-content">This is a<iclass="inline-italics">text block</i>! I have a<bclass="inline-bold">lot</b>of inline formatting options!<spanclass="inline-color"style="color: #ff4930;">Here's some text in</span><spanclass="inline-color"style="color: #00b8ff;">color</span></span></p></div><divclass="layout-row"><figureclass="image-block"><divclass="image-container"><imgalt="image"class="image"loading="lazy"sizes="(max-width: 540px) 33vh, 180px"srcset="..."></div></figure><figureclass="image-block"><divclass="image-container"><imgalt="image"class="image"loading="lazy"sizes="(max-width: 540px) 33vh, 180px"srcset="..."></div></figure><figureclass="image-block"><divclass="image-container"><imgalt="image"class="image"loading="lazy"sizes="(max-width: 540px) 33vh, 180px"srcset="..."></div></figure></div><divclass="layout-row"><figureclass="image-block"><divclass="image-container"><imgalt="image"class="image"loading="lazy"sizes="(max-width: 540px) 50vh, 270px"srcset="..."></div></figure><figureclass="image-block"><divclass="image-container"><imgalt="image"class="image"loading="lazy"sizes="(max-width: 540px) 50vh, 270px"srcset="..."></div></figure></div><divclass="layout-row"><divclass="link-block"><aclass="link-block-link"href="https://href.li/?https://example.com"><divclass="link-block-title"><span>Example Domain</span></div><divclass="link-block-description-container"><divclass="link-block-subtitles"><span><span>example.com</span></span></div></div></a></div></div><divclass="layout-row"><pclass="text-block">This is a link</p></div>OutputMake sure to import the CSS fromnpf_renderer.utils.BASIC_LAYOUT_CSS!withopen("basic_layout.css","w")asfile:file.write(npf_renderer.utils.BASIC_LAYOUT_CSS)<linkrel="stylesheet"type="text/css"href="basic_layout.css">format_npfwill return placeholder HTML for any blocks it doesn't support.Unsupported blocks<divclass="post-body"><pclass="text-block">This text block is supported but the next block is not!</p><divclass="unsupported-content-block"><divclass="unsupported-content-block-message"><h1>Unsupported content placeholder</h1><p>Hello! I'm a placeholder for the unsupported "poll" type NPF content block. Please report me!</p></div></div></div>In the event that it cannot format anything an empty div will be returnedInstallationpipinstallnpf-rendererAdvancedURL HandlingYou can pass in a custom URL handler function toformat_npfto replace any links within the NPF tree.defurl_handler(url):url=urllib.parse.urlparse(url)ifurl.hostname.endswith("example.com"):returnurl._replace(netloc="other.example.com").geturl()format_npf(contents,layouts,url_handler=url_handler)PollsAs polls require an additional request in order to fetch results,npf-rendererby default can only render a very basic poll without any votes attached.You can however provide the data necessary fornpf-rendererto populate polls by passing in a callback function toformat_npfthat takes apoll_idargument.Be sure to have the blog name and the post ID at hand too, as Tumblr's API requires all three to fetch poll results.The data returned should be in the form:{
  "results": {
    // answer_id => vote_count
    "9a025e86-2f02-452e-99d3-b4c0fd9afd48": 123,
    "fad65faf-06d3-4a24-85a9-47c096ab07e3": 321
  },
  "timestamp": 1706642448
}Example:defcreate_callback(blog_name,post_id)defpoll_callback(poll_id):initial_results=request_poll_results(blog_name,post_id,poll_id)returninitial_results["response"]returnpoll_callbacknpf_renderer.format_npf(content,layout,poll_result_callback=create_callback(blog_name,post_id))Features(True as of master branch)Text blocksImage blocksLink BlocksAudio BlocksVideo BlocksPolls BlocksLayoutsAttributions