Meta Now Lets Anyone Use Your Instagram Photos in AI Images—Unless You Opt Out
As part of Meta’s Muse Image model rollout, Instagram users with public accounts need to opt out to block AI generations of their content.
找到 2988 篇相关文章
As part of Meta’s Muse Image model rollout, Instagram users with public accounts need to opt out to block AI generations of their content.
Redpanda is a Kafka-API-compatible streaming platform written in C++ with no JVM and no ZooKeeper. This guide installs Redpanda on Ubuntu 24.04, secures it with a Let's Encrypt certificate and SASL/SCRAM authentication, tunes the kernel for production, verifies with a producer/consumer test, and exposes Redpanda Console behind Nginx basic auth. By the end, you'll have a secured, production-tuned single-node Redpanda cluster with a web console. Prerequisite: Ubuntu 24.04 server sized per Redpanda's CPU/memory requirements , non-root sudo user, and a domain A record (e.g. redpanda.example.com ). Install Redpanda $ sudo apt update $ curl -1sLf 'https://dl.redpanda.com/nzc4ZYQK3WRGd9sy/redpanda/cfg/setup/bash.deb.sh' | sudo -E bash Warning: Only run vendor setup scripts you trust — piped curl | sudo bash runs with root privileges. $ sudo apt install redpanda -y $ rpk --version Open the Firewall Port Service Purpose 9092 Kafka API Producer/consumer traffic 8082 Pandaproxy (HTTP) REST access for non-Kafka clients 8081 Schema Registry Avro/Protobuf schema versioning 9644 Admin API Monitoring, config, health checks 33145 Internal RPC Inter-node communication $ sudo ufw allow 9092,8082,8081,9644,33145/tcp $ sudo ufw allow 80/tcp $ sudo ufw allow 443/tcp $ sudo ufw reload Issue a Let's Encrypt Certificate Redpanda ships with plaintext networking by default, fine for a lab, not for anything else. $ sudo apt install certbot -y $ DOMAIN = redpanda.example.com $ EMAIL = admin@example.com $ sudo certbot certonly --standalone -d $DOMAIN --non-interactive --email $EMAIL Certbot stores certs under /etc/letsencrypt/live , readable only by root. Redpanda runs as its own redpanda user, so copy the certs into a dedicated directory: $ sudo mkdir /etc/redpanda/certs $ sudo cp /etc/letsencrypt/live/ $DOMAIN /fullchain.pem /etc/redpanda/certs/node.crt $ sudo cp /etc/letsencrypt/live/ $DOMAIN /privkey.pem /etc/redpanda/certs/node.key $ sudo cp /etc/letsencrypt/live/ $DOMAIN /chain.pem /etc/re
Google Vertex AI is Google Cloud's managed ML platform with experiment tracking, training jobs, pipelines, a model registry, and endpoints, but it locks you into GCP-specific APIs and per-use billing. ClearML is an open-source MLOps platform that covers the same ground on any Docker host or Kubernetes cluster, capturing training metrics automatically with no code changes and keeping every byte of data on infrastructure you control. This guide deploys ClearML Server with Docker Compose and Traefik, registers an agent, runs an experiment, builds a pipeline, runs a hyperparameter sweep, and deploys a Triton-served model. By the end, you'll have a self-hosted Vertex AI replacement covering the full ML lifecycle. Vertex AI → ClearML Mapping GCP Vertex AI ClearML Equivalent Purpose Vertex AI Workbench ClearML Web UI Browser-based monitoring and configuration Vertex AI Experiments ClearML Experiment Manager Automatic hyperparameter/metric/artifact tracking Vertex AI Training Job ClearML Agent + Tasks Any machine becomes a remote worker via queues Vertex AI Pipelines ClearML Pipelines Python-native DAGs, no separate compile step Vertex AI Model Registry ClearML Model Repository Versioned models with full lineage Vertex AI Endpoints ClearML Serving (Triton) Self-hosted inference with canary/A-B support Cloud Monitoring/Logging ClearML Scalars/Plots Built-in metrics and hardware dashboards Prerequisite: Ubuntu host with Docker + Compose, DNS A records for app.clearml.example.com , api.clearml.example.com , files.clearml.example.com . NVIDIA Container Toolkit if you'll run GPU agents. Deploy the ClearML Server 1. Raise Elasticsearch's virtual memory limit and restart Docker: $ echo "vm.max_map_count=524288" | sudo tee /etc/sysctl.d/99-clearml.conf $ sudo sysctl --system $ sudo systemctl restart docker 2. Create persistent data directories with the correct ownership: $ sudo mkdir -p /opt/clearml/ { data/elastic_7,data/mongo_4/db,data/mongo_4/configdb,data/redis,data/fileserver,
Matomo is an open-source web analytics platform that keeps full ownership of visitor data on infrastructure you control, a privacy-first alternative to Google Analytics. This guide deploys Matomo using Docker Compose with a MariaDB backend, Nginx as the entry point, and Certbot issuing the TLS certificate before the stack goes live. By the end, you'll have Matomo tracking a website with a signed HTTPS certificate at your domain. Prepare Docker $ sudo usermod -aG docker $USER $ newgrp docker Set Up the Project 1. Create the project directory: $ mkdir matomo && cd matomo 2. Open the firewall: $ sudo ufw allow 80/tcp $ sudo ufw allow 443/tcp 3. Create the environment file: $ nano .env MYSQL_ROOT_PASSWORD = your_strong_mysql_root_password MYSQL_PASSWORD = your_strong_mysql_matomo_password Use passwords of at least 16 characters. 4. Create the Nginx config: $ mkdir nginx $ nano nginx/matomo.conf server { listen 80 ; server_name matomo.example.com ; location /.well-known/acme-challenge/ { root /var/www/certbot ; } location / { return 301 https:// $host$request_uri ; } } server { listen 443 ssl http2 ; server_name matomo.example.com ; ssl_certificate /etc/letsencrypt/live/matomo.example.com/fullchain.pem ; ssl_certificate_key /etc/letsencrypt/live/matomo.example.com/privkey.pem ; location / { proxy_pass http://app:80 ; proxy_set_header Host $host ; proxy_set_header X-Real-IP $remote_addr ; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ; proxy_set_header X-Forwarded-Proto $scheme ; } } Replace every matomo.example.com occurrence with your domain — it appears in both server blocks and the certificate paths. Deploy with Docker Compose $ nano docker-compose.yaml services : db : image : mariadb:11.4 command : --max-allowed-packet=64MB restart : always volumes : - matomo-db-data:/var/lib/mysql environment : - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} - MYSQL_DATABASE=matomo - MYSQL_USER=matomo - MYSQL_PASSWORD=${MYSQL_PASSWORD} networks : - matomo-net app : image
I rebuilt aesencryption.net so text AES (128/192/256) runs fully in the browser - the key and plaintext never leave the page. The hard part is staying byte-compatible with common server-side AES libraries (mode, IV, padding, base64 output), so I ship copy-paste equivalents in PHP, Java, Python, Go, Rust, Kotlin and JS. Live tool (mine, free): https://aesencryption.net - feedback on the crypto choices welcome. My own site.
Plausible Analytics is an open-source, self-hosted web analytics tool that tracks traffic without cookies or personal data collection, a privacy-first alternative to Google Analytics. This guide deploys Plausible Community Edition using Docker Compose, fronts it with Nginx, and secures it with a Let's Encrypt certificate. By the end, you'll have Plausible tracking a website's traffic securely at your domain. Configure the Environment 1. Clone the Community Edition repo: $ mkdir ~/plausible $ cd ~/plausible $ git clone https://github.com/plausible/community-edition.git $ cd community-edition 2. Generate a secret key: $ openssl rand -base64 64 | tr -d '\n' 3. Create the environment file: $ nano .env ADMIN_USER_EMAIL = admin@example.com ADMIN_USER_NAME = admin ADMIN_USER_PWD = ADMIN_PASSWORD BASE_URL = https://plausible.example.com SECRET_KEY_BASE = YOUR_SECRET_KEY_BASE DATABASE_URL = postgres://postgres:postgres@plausible_db:5432/plausible_db CLICKHOUSE_DATABASE_URL = http://plausible_events_db:8123/plausible_events_db Fill in your email, a strong admin password, your domain, and the secret key generated above. 4. Expose the app port via a Compose override (auto-merged with compose.yml ): $ nano compose.override.yaml services : plausible : ports : - 127.0.0.1:8000:8000 Deploy with Docker Compose $ docker compose up -d $ docker compose ps Confirm the app, Postgres, and ClickHouse (events DB) containers are all running. Front with Nginx and Let's Encrypt 1. Install Nginx: $ sudo apt update $ sudo apt install nginx -y 2. Create the virtual host: $ sudo nano /etc/nginx/sites-available/plausible.conf server { listen 80 ; listen [::]:80 ; server_name plausible.example.com ; access_log /var/log/nginx/plausible.access.log ; error_log /var/log/nginx/plausible.error.log ; location / { proxy_pass http://127.0.0.1:8000 ; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ; proxy_http_version 1.1 ; proxy_set_header Upgrade $http_upgrade ; proxy_set_header Connection "Upgr
Momentic, the company behind an AI-driven software testing platform, recently rearchitected its caching system to handle over 2 million queries per day across 20 billion total entries, while maintaining an average response latency of around 250 ms. This improvement was made possible by transitioning from PostgreSQL to the column-oriented database ClickHouse. By Sergio De Simone
If you've ever built a search endpoint, you've hit this wall. Your query has filters, sort orders, a nested set of facets, maybe a geo bounding box. It doesn't fit in a URL, and cramming it into query string params is ugly and fragile. So you reach for POST /search , send the whole thing as a JSON body, and quietly accept that you've just lied about what the request does. It's not creating anything. It's a read. But POST is the only tool that lets you attach a body without fighting the platform. That gap finally got filled. In June 2026 the IETF published RFC 10008 , which defines the HTTP QUERY method: a new verb built for exactly this case. The two bad options Every read that needs structured input has been stuck choosing between GET and POST, and both are wrong in their own way. GET is the semantically correct choice. It's safe (the client isn't asking to change anything), it's idempotent (retrying it is fine), and it's cacheable. The problem is the body. RFC 9110 is explicit that content in a GET request has no defined semantics , and sending one may cause some implementations to reject the request. So your query has to live in the URI, where you run into unknown length limits across proxies and servers, encoding overhead, and the query landing in access logs and browser history. POST solves the body problem and creates a new one. It carries any payload you want, but it's neither safe nor idempotent by definition. Intermediaries won't cache it, clients won't retry it automatically after a dropped connection, and anything inspecting traffic has to assume the request might have side effects. You get the body, you lose everything that made the request honest. QUERY is the missing third option: a method that carries a body and keeps the semantics of a read. What QUERY actually is The spec, authored by Julian Reschke, James Snell , and Mike Bishop, describes it in one sentence: A QUERY requests that the request target process the enclosed content in a safe and idempo
A story about how migrating an interactive office map editor turned into an engineering investigation involving Fabric.js, tainted canvas , and an architecture that's finally easy to extend. In most software projects, one sentence usually makes every developer nervous: "Let's rewrite this module from scratch." It often means months of development, regression risks, and endless architecture discussions. Our project was no different. We develop, a workspace management platform that allows companies to manage office spaces and book desks. One of its core features is an interactive office map editor, where administrators upload floor plans, place desks and meeting rooms, and publish maps for employees. Over the years, this editor slowly evolved into a real monolith. And the problem wasn't simply the number of lines of code. Where It All Started The editor dated back to the AngularJS era. The main component had gradually grown into a single file responsible for almost everything: loading maps working with Fabric.js CRUD operations keyboard shortcuts dialogs saving event handling The main editor component alone contained nearly 2,270 lines of code . Behind it lived another codebase — the map engine itself. Almost 20,000 lines of TypeScript spread across more than 230 files. One of the biggest architectural issues was an infinite rendering loop. fabric . util . requestAnimFrame (() => this . tick ()); Even when the user wasn't interacting with the editor, rendering continued forever. It worked. But every new feature became more expensive to build. Why We Decided to Rewrite It The motivation wasn't AngularJS itself. The real reason was business requirements. The product needed completely new capabilities: map drafts safe publishing high-quality printing multiple workspace modes easier support for new object types Every new feature pushed harder against the existing architecture. Eventually it became obvious: We weren't fighting individual bugs anymore. We were fighting the
Claude Cowork now keeps working on tasks even after you close your laptop. It’s part of a larger push toward smartphone-controlled agents.
Everyone knows Next.js is the default full-stack JavaScript framework in 2026. The job ads say so,...
We're back with another installment of the DEV Weekend Challenge ! If you missed the earlier editions, these are short-form, high-energy challenges designed to fit right into your weekend. We're giving you the heads-up now so you can clear your schedule! How It Works Our challenge prompt will be revealed at launch. Follow #weekendchallenge for updates. You can also keep an eye on the DEV Weekend Challenge page or look out for the official announcement post from the DEV Team . From there, you'll have the entire weekend to build, document, and submit your project. That's all there is to it! Because our community spans every timezone on the planet, we've set the window so that everyone around the world gets at least a full weekend to participate. Important Dates Launch Time: July 10 at 2:00 AM UTC Submissions Due: July 13 at 6:59 AM UTC Here's what that looks like across a few timezones: Timezone Launch Time (Local) Submissions Due (Local) PDT Thursday, Jul 9 at 7:00 PM Sunday, Jul 12 at 11:59 PM EDT Thursday, Jul 9 at 10:00 PM Monday, Jul 13 at 2:59 AM GMT Friday, Jul 10 at 2:00 AM Monday, Jul 13 at 6:59 AM CEST Friday, Jul 10 at 4:00 AM Monday, Jul 13 at 8:59 AM IST Friday, Jul 10 at 7:30 AM Monday, Jul 13 at 12:29 PM JST Friday, Jul 10 at 11:00 AM Monday, Jul 13 at 3:59 PM AEST Friday, Jul 10 at 12:00 PM Monday, Jul 13 at 4:59 PM While the window technically spans more than 48 hours, our goal is to ensure everyone has a full, uninterrupted weekend to work on their project regardless of where they live. What else is happening? Mark your calendars for the upcoming Summer Bug Smash . Bug Smash - Register Now We can't wait to see what you build!
Read Committed: snapshot mỗi statement, và vì sao hai SELECT trong cùng transaction có thể trả khác nhau READ COMMITTED là isolation level mặc định của PostgreSQL, và là level mà phần lớn workload OLTP đang chạy mà không biết. Khác với mô hình "transaction lấy một snapshot rồi giữ nguyên" mà nhiều dev tưởng tượng từ MVCC, ở Read Committed mỗi statement lấy một snapshot mới tại thời điểm statement bắt đầu , không phải tại thời điểm BEGIN . Hậu quả thực tế: hai SELECT liên tiếp trong cùng một transaction có thể trả về dữ liệu khác nhau nếu giữa hai lần đó có transaction khác commit. Đây là non-repeatable read — đúng spec của Read Committed, không phải bug — và là nguồn của một class lỗi rất hay gặp: code đọc một giá trị, ra quyết định, rồi cập nhật dựa trên giá trị đã đọc, trong khi giá trị thực tế đã thay đổi. Cơ chế hoạt động Một transaction ở Read Committed không có transaction-level snapshot . Khi mỗi statement (mỗi SELECT , UPDATE , DELETE , INSERT ... SELECT ...) bắt đầu thực thi, backend lấy một snapshot mới gồm xmin , xmax và xip list — chính cái snapshot quyết định row version nào "visible" theo MVCC. Statement chỉ thấy: row có xmin đã commit trước thời điểm statement bắt đầu , và xmax chưa tồn tại hoặc thuộc một transaction chưa commit / đã abort. Ngay sau khi statement kết thúc, snapshot đó bị bỏ. Statement kế tiếp lấy snapshot mới — nếu trong khoảng giữa có transaction khác commit, statement này sẽ thấy dữ liệu mới đó. -- T1 BEGIN ; -- KHÔNG lấy snapshot ở đây SELECT balance FROM accounts WHERE id = 1 ; -- snapshot S1 -> trả 1000 -- ... T2 chạy: UPDATE accounts SET balance=500 WHERE id=1; COMMIT; SELECT balance FROM accounts WHERE id = 1 ; -- snapshot S2 -> trả 500 COMMIT ; Với UPDATE / DELETE / SELECT ... FOR UPDATE / FOR NO KEY UPDATE / FOR SHARE , Read Committed làm thêm một bước đặc biệt mà SELECT thường không làm: nếu target row bị một transaction khác đang lock (chưa commit), statement đợi transaction đó kết thúc. Khi unblock: nếu transaction kia ROL
You can clip a cover over the cameras, which could be a double-edged sword.
Part 1: Solving GOP Structure and Compatibility Issues Operating a digital television network isn't just about keeping channels on air—it's about maintaining quality that viewers expect and troubleshooting issues before they escalate. When something goes wrong in live broadcasting, every second counts. But how do you quickly pinpoint whether the problem lies in encoder settings, transport stream structure, or temporal metadata? This is where specialized stream analysis tools become essential. In this series of articles, we'll walk through real-world scenarios that broadcast engineers face daily and show practical approaches to diagnosing and resolving them. When File Analysis Becomes Critical While live monitoring catches issues as they happen, file-based analysis is your diagnostic microscope. Here's the typical workflow: something breaks in production, engineers capture a few minutes of the problematic stream, and now they need to understand exactly what went wrong. File analyzers serve three primary purposes: Troubleshooting: Identifying the root cause of broadcast issues Encoder optimization: Fine-tuning compression settings Quality control: Validating compliance with standards and specifications Let's explore how this works in practice with actual tools and techniques. The GOP Structure Problem Here's a scenario every broadcast engineer has encountered: legacy set-top boxes or older TV models suddenly can't play your stream. The audio works, video starts and stops, or you see freezing. The culprit? Often, it's the GOP (Group of Pictures) structure. H.264 has been around since 2003—over 20 years. Almost everything supports it, yet you'll still find legacy equipment that struggles with certain configurations. Specifically, the number of B-frames can make or break compatibility. Why B-frames matter: They enable lower bitrates while maintaining quality by increasing encoding complexity through bidirectional prediction. But this comes at a cost—a more complex refere
Modern software development thrives on rapid iteration. Organizations deploy new features, bug fixes, and infrastructure updates multiple times each day to remain competitive and respond quickly to customer needs. Continuous Integration and Continuous Delivery (CI/CD) have transformed software delivery by automating repetitive tasks and accelerating release cycles. However, speed without security creates significant risk. A fast deployment pipeline that introduces vulnerable code into production can expose organizations to data breaches, service disruptions, and compliance violations. Conversely, excessive manual security reviews can slow innovation and delay valuable releases. The solution lies in integrating security directly into the CI/CD pipeline rather than treating it as a separate checkpoint. This philosophy, commonly known as DevSecOps, enables organizations to deliver software rapidly while maintaining a strong security posture. Understanding CI/CD Pipelines What Is Continuous Integration? Continuous Integration (CI) is the practice of frequently merging code changes into a shared repository. Every commit automatically triggers builds and tests, allowing development teams to identify integration issues early instead of waiting until the end of a project. Frequent integration encourages collaboration, reduces merge conflicts, and improves overall software quality. What Is Continuous Delivery? Continuous Delivery extends Continuous Integration by ensuring that validated code is always in a deployable state. Automated testing, packaging, and release preparation make it possible to deploy new versions with minimal manual effort whenever the business is ready. What Is Continuous Deployment? Continuous Deployment goes one step further by automatically releasing approved changes to production once they pass all quality and security checks. This approach significantly shortens release cycles while requiring a high level of confidence in pipeline automation. Benefi
TL;DR — Angular 22 promoted Signal Forms from experimental to stable. This is not "Reactive Forms are dead." It's a real architectural trade-off, and this post walks through both APIs in full, with production-realistic code, so you can decide feature-by-feature instead of framework-war-by-framework-war. Table of Contents Why This Matters Now The Core Question Reactive Forms: Why It Became the Standard Full Example: Reactive Forms Login Where Reactive Forms Still Excel Signal Forms: What Actually Changed in Angular 22 Full Example: Signal Forms Login Where Signal Forms Shine Side-by-Side: Core Concepts Mapped Deep Dive: Validation Synchronous Validation Cross-Field Validation Conditional Validation with when() Async Validation Deep Dive: Dynamic and Nested Forms Nested Form Groups Dynamic Collections (FormArray-style) Deep Dive: Form State — Dirty, Touched, Errors, Submission Developer Experience and Testing Performance Considerations Interop: Migrating Without a Big-Bang Rewrite Migration Strategy for Enterprise Teams When NOT to Migrate Decision Framework FAQ Closing Thoughts Why This Matters Now With Angular 22 (released June 3, 2026), Signal Forms left experimental status and became part of the stable, supported API — alongside resource() and httpResource() . That's a meaningful milestone: it means the Angular team ran extensive internal case studies across real form-heavy applications at Google before committing to stability, and the interop story with Reactive Forms has matured enough that a big-bang rewrite is no longer the only migration path. At the same time, Angular 22 also flips two important defaults: components now use OnPush change detection by default, and zoneless change detection continues its push toward becoming the standard. Signal Forms is part of that same story — Angular's reactivity model finally speaking one dialect end-to-end, from component state to form state to async data. None of this makes Reactive Forms obsolete. It changes what "the
Redux setup is a ceremony. You create a store, compose your reducers into a root tree, wrap your app in a Provider, register middleware, and configure enhancers — all before you write a single line of feature logic. SDuX Vault™ replaces that entire ceremony with two function calls and zero root configuration. Redux Store Ceremony A typical Redux application requires several files and configuration steps before state management is operational. Here is what a minimal Redux setup looks like for a single feature: // store.ts import { createStore , combineReducers , applyMiddleware } from ' redux ' ; import thunk from ' redux-thunk ' ; import { userReducer } from ' ./reducers/userReducer ' ; const rootReducer = combineReducers ({ users : userReducer , }); export const store = createStore ( rootReducer , applyMiddleware ( thunk ) ); // App.tsx — Provider wrapper required import { Provider } from ' react-redux ' ; import { store } from ' ./store ' ; function App () { return ( < Provider store = { store } > < UserList /> < /Provider > ); } That is 20+ lines of configuration across multiple files — and it only covers one feature. Add a second feature and you are back in the combineReducers file, composing another slice into the tree. Add middleware and you are threading enhancers through applyMiddleware . Add DevTools and you are composing composeWithDevTools on top. Every new feature touches the root configuration. Redux Requirement What It Does createStore() Creates the single global store instance combineReducers() Composes feature reducers into a root tree applyMiddleware() Registers middleware (thunk, saga, etc.) Provider Makes the store available to all components via context composeWithDevTools() Enables Redux DevTools integration ⚠️ Warning: Every entry in that table is root-level configuration. Adding a new feature means editing the root reducer composition, possibly the middleware stack, and potentially the Provider hierarchy. Root configuration is a shared depende
I came into this as a graphic designer, not a software engineer. I didn't have a computer science background, and a lot of what BrandStack needed — authentication, databases, payments, deployment — was new territory for me when I started. What made it possible wasn't some shortcut. It was breaking the problem down into pieces I could actually learn: how user accounts work, how a database should be structured so one person's data never leaks into another's, how to move from test payments to real ones without breaking checkout for actual customers. I made real mistakes along the way. Early on, every user shared the same underlying brand data because I hadn't scoped the database correctly to each account — a serious bug that I only caught by testing with two separate accounts myself. Finding and fixing that taught me more about proper application architecture than any tutorial could have. I don't think being a designer first is a disadvantage for building product. If anything, it means the interface and the experience get real attention, not just the backend logic. But it does mean being honest about what you don't know yet, and being willing to slow down and actually understand a problem instead of copying a fix you don't understand. BrandStack is still a work in progress. But it's a real, working product — built by someone who had to learn most of this from scratch, in public, one bug at a time.
How to Monitor Website Changes Automatically I run a few websites and need to know immediately when something breaks. A CSS regression, a broken layout, a missing section. Manual checking doesn't scale, and text-based monitoring misses visual issues. The {{screenshot-diff}} on Apify takes two screenshots and produces a pixel-level comparison with an overlay showing exactly what changed. How It Works Take a baseline screenshot of the correct state. Then take a current screenshot of the live page. The actor compares pixel by pixel and returns a diff image with changed pixels highlighted, plus a percentage telling you how much changed. import requests , time API_TOKEN = " YOUR_APIFY_TOKEN " def capture_screenshot ( url ): resp = requests . post ( " https://api.apify.com/v2/acts/weeknds~website-screenshot-api/runs " , headers = { " Authorization " : f " Bearer { API_TOKEN } " }, json = { " url " : url , " fullPage " : True } ) run_id = resp . json ()[ " data " ][ " id " ] time . sleep ( 15 ) items = requests . get ( f " https://api.apify.com/v2/acts/weeknds~website-screenshot-api/runs/ { run_id } /dataset/items " , headers = { " Authorization " : f " Bearer { API_TOKEN } " } ). json () return items [ 0 ][ " screenshotUrl " ] def compare_screenshots ( baseline_url , current_url ): resp = requests . post ( " https://api.apify.com/v2/acts/weeknds~screenshot-comparison-tool/runs " , headers = { " Authorization " : f " Bearer { API_TOKEN } " }, json = { " baselineImageUrl " : baseline_url , " currentImageUrl " : current_url , " threshold " : 0.01 } ) run_id = resp . json ()[ " data " ][ " id " ] time . sleep ( 10 ) items = requests . get ( f " https://api.apify.com/v2/acts/weeknds~screenshot-comparison-tool/runs/ { run_id } /dataset/items " , headers = { " Authorization " : f " Bearer { API_TOKEN } " } ). json () return items [ 0 ] baseline = capture_screenshot ( " https://mysite.com " ) current = capture_screenshot ( " https://mysite.com " ) result = compare_screenshots ( b