<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Fedor Pasynkov</title>
    <description>The latest articles on DEV Community by Fedor Pasynkov (@fedor-pasynkov).</description>
    <link>https://dev.to/fedor-pasynkov</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3117364%2Fcb435760-cadf-4f50-8ddb-5e15d55de692.jpg</url>
      <title>DEV Community: Fedor Pasynkov</title>
      <link>https://dev.to/fedor-pasynkov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fedor-pasynkov"/>
    <language>en</language>
    <item>
      <title>How To Control Brightness In Ubuntu 22.04?</title>
      <dc:creator>Fedor Pasynkov</dc:creator>
      <pubDate>Mon, 02 Jun 2025 17:05:19 +0000</pubDate>
      <link>https://dev.to/fedor-pasynkov/time-based-brightness-control-for-ubuntu-2204-a-complete-guide-65h</link>
      <guid>https://dev.to/fedor-pasynkov/time-based-brightness-control-for-ubuntu-2204-a-complete-guide-65h</guid>
      <description>&lt;p&gt;Managing screen brightness automatically based on the time of day can significantly improve your computing experience and reduce eye strain. In this guide, we'll create a script that automatically adjusts your screen brightness throughout the day, simulating natural light patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We'll Build
&lt;/h2&gt;

&lt;p&gt;Our script will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Morning (7:00-9:00 AM)&lt;/strong&gt;: Gradually increase brightness from 2% to 100%&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Day (9:00 AM-5:00 PM)&lt;/strong&gt;: Maintain maximum brightness (100%)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evening (5:00-11:59 PM)&lt;/strong&gt;: Gradually decrease brightness from 100% to 2%&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Night (12:00-6:59 AM)&lt;/strong&gt;: Maintain minimum brightness (2%)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Ubuntu 22.04 (should work on other Ubuntu versions too)&lt;/li&gt;
&lt;li&gt;X11 Server&lt;/li&gt;
&lt;li&gt;Terminal access with sudo privileges&lt;/li&gt;
&lt;li&gt;A laptop or monitor that supports brightness control&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Install brightnessctl
&lt;/h2&gt;

&lt;p&gt;First, we need to install &lt;code&gt;brightnessctl&lt;/code&gt;, a utility that allows us to control screen brightness from the command line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;brightnessctl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the installation by checking available brightness controls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brightnessctl &lt;span class="nt"&gt;--list&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see output similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Available devices:
Device 'intel_backlight' of class 'backlight':
    Current brightness: 850 (80%)
    Max brightness: 1060
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Create the Brightness Control Script
&lt;/h2&gt;

&lt;p&gt;Create a new script file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano ~/TimeBasedBrightControl.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# Get current hour and minutes (remove leading zeros)&lt;/span&gt;
&lt;span class="nv"&gt;current_hour&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%-H&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;current_minute&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%-M&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Add debug output&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Current time: &lt;/span&gt;&lt;span class="nv"&gt;$current_hour&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$current_minute&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Function to calculate brightness&lt;/span&gt;
calculate_brightness&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;# If time is between 17:00 and 23:59&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$current_hour&lt;/span&gt; &lt;span class="nt"&gt;-ge&lt;/span&gt; 17 &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$current_hour&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; 24 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="c"&gt;# Total minutes from 17:00 to 24:00&lt;/span&gt;
        &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;total_minutes_decrease&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;420  &lt;span class="c"&gt;# 7 hours * 60 minutes&lt;/span&gt;

        &lt;span class="c"&gt;# Current minutes since 17:00&lt;/span&gt;
        &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;minutes_since_17&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;current_hour &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;17&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; current_minute &lt;span class="k"&gt;))&lt;/span&gt;

        &lt;span class="c"&gt;# Linear interpolation from 100% to 2%&lt;/span&gt;
        &lt;span class="c"&gt;# Formula: 100 - (minutes_since_17 / total_minutes_decrease) * (100 - 2)&lt;/span&gt;
        &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;brightness&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;minutes_since_17 &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;98&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; total_minutes_decrease&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;))&lt;/span&gt;

        &lt;span class="c"&gt;# Output debug information to stderr&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Time &lt;/span&gt;&lt;span class="nv"&gt;$current_hour&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$current_minute&lt;/span&gt;&lt;span class="s2"&gt;. Decreasing brightness."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Minutes after 17:00: &lt;/span&gt;&lt;span class="nv"&gt;$minutes_since_17&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Calculated brightness: &lt;/span&gt;&lt;span class="nv"&gt;$brightness&lt;/span&gt;&lt;span class="s2"&gt;%"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2

        &lt;span class="c"&gt;# Limit minimum brightness to 2%&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$brightness&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; 2 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
            &lt;/span&gt;&lt;span class="nv"&gt;brightness&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2
        &lt;span class="k"&gt;fi&lt;/span&gt;

        &lt;span class="c"&gt;# Return only numeric value&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$brightness&lt;/span&gt;
    &lt;span class="c"&gt;# If time is between 00:00 and 06:59&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$current_hour&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; 7 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Time &lt;/span&gt;&lt;span class="nv"&gt;$current_hour&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$current_minute&lt;/span&gt;&lt;span class="s2"&gt;. Minimum brightness."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
        &lt;span class="nb"&gt;echo &lt;/span&gt;2
    &lt;span class="c"&gt;# If time is between 07:00 and 08:59&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$current_hour&lt;/span&gt; &lt;span class="nt"&gt;-ge&lt;/span&gt; 7 &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$current_hour&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; 9 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="c"&gt;# Total minutes from 07:00 to 09:00&lt;/span&gt;
        &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;total_minutes_increase&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;120 &lt;span class="c"&gt;# 2 hours * 60 minutes&lt;/span&gt;

        &lt;span class="c"&gt;# Current minutes since 07:00&lt;/span&gt;
        &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;minutes_since_7&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;current_hour &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; current_minute &lt;span class="k"&gt;))&lt;/span&gt;

        &lt;span class="c"&gt;# Linear interpolation from 2% to 100%&lt;/span&gt;
        &lt;span class="c"&gt;# Formula: 2 + (minutes_since_7 / total_minutes_increase) * (100 - 2)&lt;/span&gt;
        &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;brightness&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;minutes_since_7 &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;98&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; total_minutes_increase&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;))&lt;/span&gt;

        &lt;span class="c"&gt;# Output debug information to stderr&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Time &lt;/span&gt;&lt;span class="nv"&gt;$current_hour&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$current_minute&lt;/span&gt;&lt;span class="s2"&gt;. Increasing brightness."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Minutes after 07:00: &lt;/span&gt;&lt;span class="nv"&gt;$minutes_since_7&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Calculated brightness: &lt;/span&gt;&lt;span class="nv"&gt;$brightness&lt;/span&gt;&lt;span class="s2"&gt;%"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2

        &lt;span class="c"&gt;# Limit maximum brightness to 100% (just in case)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$brightness&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 100 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
            &lt;/span&gt;&lt;span class="nv"&gt;brightness&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;100
        &lt;span class="k"&gt;fi&lt;/span&gt;

        &lt;span class="c"&gt;# Return only numeric value&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$brightness&lt;/span&gt;
    &lt;span class="c"&gt;# Otherwise (time between 09:00 and 16:59)&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Time &lt;/span&gt;&lt;span class="nv"&gt;$current_hour&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$current_minute&lt;/span&gt;&lt;span class="s2"&gt;. Maximum brightness."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
        &lt;span class="nb"&gt;echo &lt;/span&gt;100
    &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Get calculated brightness (only number)&lt;/span&gt;
&lt;span class="nv"&gt;brightness&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;calculate_brightness&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Add debug output&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Final brightness: &lt;/span&gt;&lt;span class="nv"&gt;$brightness&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# If brightness is defined, set it&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$brightness&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Setting brightness: &lt;/span&gt;&lt;span class="nv"&gt;$brightness&lt;/span&gt;&lt;span class="s2"&gt;%"&lt;/span&gt;
    &lt;span class="nb"&gt;sudo &lt;/span&gt;brightnessctl &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$brightness&lt;/span&gt;&lt;span class="s2"&gt;%"&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Brightness not defined (error in calculations)"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Make the Script Executable
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ~/TimeBasedBrightControl.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Test the Script
&lt;/h2&gt;

&lt;p&gt;Run the script manually to ensure it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/TimeBasedBrightControl.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see output like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Current time: 14:30
Time 14:30. Maximum brightness.
Final brightness: 100
Setting brightness: 100%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Handle Sudo Requirements
&lt;/h2&gt;

&lt;p&gt;Since we'll be running the script through &lt;code&gt;sudo crontab -e&lt;/code&gt; (root crontab), the script will automatically have the necessary privileges to control brightness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Set Up Automation with Cron
&lt;/h2&gt;

&lt;p&gt;⚠️ &lt;strong&gt;IMPORTANT&lt;/strong&gt;: You must use &lt;code&gt;sudo crontab -e&lt;/code&gt; instead of regular &lt;code&gt;crontab -e&lt;/code&gt; for the brightness control to work properly. Regular user cron jobs don't have the necessary permissions to control system brightness.&lt;/p&gt;

&lt;p&gt;Open the root crontab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;crontab &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following line to run the script every 5 minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;*&lt;/span&gt;/5 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /bin/bash &lt;span class="s2"&gt;"/home/username/TimeBasedBrightControl.sh"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;username&lt;/code&gt; with your actual username. This will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run the script every 5 minutes using &lt;code&gt;/bin/bash&lt;/code&gt; explicitly&lt;/li&gt;
&lt;li&gt;Execute with root privileges (necessary for brightness control)&lt;/li&gt;
&lt;li&gt;Log output to &lt;code&gt;~/brightness.log&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Capture both standard output and errors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example with actual path:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;*&lt;/span&gt;/5 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /bin/bash &lt;span class="s2"&gt;"/home/fedor/TimeBasedBrightControl.sh"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 7: Monitor the Script
&lt;/h2&gt;

&lt;p&gt;Check if the cron job is working:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ~/brightness.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see entries being added every 5 minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Brightness Calculation
&lt;/h2&gt;

&lt;p&gt;The script uses linear interpolation to smoothly transition brightness:&lt;/p&gt;

&lt;h3&gt;
  
  
  Evening Transition (17:00-23:59)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Duration&lt;/strong&gt;: 7 hours (420 minutes)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Range&lt;/strong&gt;: 100% → 2%&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Formula&lt;/strong&gt;: &lt;code&gt;100 - (minutes_since_17 * 98 / 420)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Morning Transition (07:00-08:59)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Duration&lt;/strong&gt;: 2 hours (120 minutes)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Range&lt;/strong&gt;: 2% → 100%&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Formula&lt;/strong&gt;: &lt;code&gt;2 + (minutes_since_7 * 98 / 120)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Customization Options
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Adjust Time Ranges
&lt;/h3&gt;

&lt;p&gt;To change the time periods, modify these conditions in the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Change evening start time (currently 17:00)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$current_hour&lt;/span&gt; &lt;span class="nt"&gt;-ge&lt;/span&gt; 17 &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$current_hour&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; 24 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;

&lt;span class="c"&gt;# Change morning start time (currently 07:00)&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$current_hour&lt;/span&gt; &lt;span class="nt"&gt;-ge&lt;/span&gt; 7 &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$current_hour&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; 9 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;

&lt;span class="c"&gt;# Change night end time (currently 06:59)&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$current_hour&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; 7 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adjust Brightness Levels
&lt;/h3&gt;

&lt;p&gt;To change minimum/maximum brightness levels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Change minimum brightness (currently 2%)&lt;/span&gt;
&lt;span class="nb"&gt;echo &lt;/span&gt;2

&lt;span class="c"&gt;# In the interpolation formulas, change the range:&lt;/span&gt;
&lt;span class="c"&gt;# Currently: 98 (which is 100-2)&lt;/span&gt;
&lt;span class="c"&gt;# For 5%-95% range, use: 90 (which is 95-5)&lt;/span&gt;
&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;brightness&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;minutes_since_7 &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;90&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; total_minutes_increase&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Change Update Frequency
&lt;/h3&gt;

&lt;p&gt;Modify the cron schedule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Every minute&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /home/username/TimeBasedBrightControl.sh

&lt;span class="c"&gt;# Every 10 minutes&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt;/10 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /home/username/TimeBasedBrightControl.sh

&lt;span class="c"&gt;# Every 30 minutes&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt;/30 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /home/username/TimeBasedBrightControl.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Script Not Running
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Check if cron is running: &lt;code&gt;sudo systemctl status cron&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Verify root crontab syntax: &lt;code&gt;sudo crontab -l&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check permissions: &lt;code&gt;ls -la ~/TimeBasedBrightControl.sh&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Brightness Not Changing
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Verify brightnessctl works: &lt;code&gt;sudo brightnessctl set 50%&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check if script runs manually: &lt;code&gt;sudo /bin/bash "/home/username/TimeBasedBrightControl.sh"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Review log file: &lt;code&gt;cat ~/brightness.log&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Permission Issues
&lt;/h3&gt;

&lt;p&gt;If you get permission errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Make sure the script has proper permissions&lt;/span&gt;
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ~/TimeBasedBrightControl.sh

&lt;span class="c"&gt;# Check if running with sudo crontab&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;crontab &lt;span class="nt"&gt;-l&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;TimeBasedBrightControl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This time-based brightness control system provides a smooth, automated way to manage screen brightness throughout the day. The linear interpolation ensures gradual transitions that are easy on the eyes, while the cron-based automation means you'll never have to manually adjust brightness again.&lt;/p&gt;

&lt;p&gt;The script is highly customizable, allowing you to adjust time ranges, brightness levels, and update frequencies to match your specific needs and schedule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ready Script
&lt;/h2&gt;

&lt;p&gt;You can find the complete working script in this repository: &lt;a href="https://github.com/pefbrute/TimeBasedBrightControl" rel="noopener noreferrer"&gt;TimeBasedBrightControl&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Found this guide helpful? Feel free to customize the script for your needs and share your improvements in the comments below!&lt;/em&gt; &lt;/p&gt;

</description>
      <category>ubuntu</category>
      <category>brightness</category>
      <category>time</category>
      <category>bash</category>
    </item>
    <item>
      <title>How to Make Your Linux Laptop Auto-Shutdown and Show Notifications Based on Battery Level?</title>
      <dc:creator>Fedor Pasynkov</dc:creator>
      <pubDate>Mon, 02 Jun 2025 14:30:26 +0000</pubDate>
      <link>https://dev.to/fedor-pasynkov/how-to-make-your-linux-laptop-auto-shutdown-and-show-notifications-based-on-battery-level-59i6</link>
      <guid>https://dev.to/fedor-pasynkov/how-to-make-your-linux-laptop-auto-shutdown-and-show-notifications-based-on-battery-level-59i6</guid>
      <description>&lt;p&gt;&lt;strong&gt;⚠️ Tested on Ubuntu 22.04 with X Server - May require adjustments for other distributions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Have you ever left your laptop plugged in for hours, slowly damaging the battery? Or worse, forgotten to charge it until it completely dies? This comprehensive guide will help you create a smart battery monitoring system that automatically manages your laptop's charging cycle and prevents battery damage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;💾 Ready to use? &lt;a href="https://github.com/pefbrute/AutoBattOff" rel="noopener noreferrer"&gt;Download the complete script from GitHub&lt;/a&gt; and skip to Step 4!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 What This Script Does
&lt;/h2&gt;

&lt;p&gt;Our battery monitoring script provides four levels of protection:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;🔴 Critical Low (≤39%)&lt;/strong&gt;: Automatically shuts down the laptop to prevent deep discharge&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🟡 Low Warning (40-49%)&lt;/strong&gt;: Shows urgent notification to plug in charger&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🟠 High Warning (80-89%)&lt;/strong&gt;: Notifies to unplug charger during charging&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🔴 Critical High (≥90%)&lt;/strong&gt;: Automatically shuts down to prevent overcharging&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  📋 Prerequisites
&lt;/h2&gt;

&lt;h3&gt;
  
  
  System Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Operating System&lt;/strong&gt;: Ubuntu 22.04 (tested) - other Linux distributions may require modifications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Display Server&lt;/strong&gt;: X Server (X11)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Required Packages
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Update package list&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update

&lt;span class="c"&gt;# Install required dependencies&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;mpg123 yad

&lt;span class="c"&gt;# mpg123: for audio notifications&lt;/span&gt;
&lt;span class="c"&gt;# yad: for GUI notifications (Yet Another Dialog)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🚀 Step 1: Project Setup
&lt;/h2&gt;

&lt;p&gt;Create the project directory and navigate to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/AutoBattOff
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/AutoBattOff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔧 Step 2: Create Configuration File
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;battery_config.env&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Battery thresholds (adjust according to your needs)&lt;/span&gt;
&lt;span class="nv"&gt;LOW_BATTERY_THRESHOLD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;39
&lt;span class="nv"&gt;CRITICAL_LOW_BATTERY_THRESHOLD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;49
&lt;span class="nv"&gt;HIGH_BATTERY_THRESHOLD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80
&lt;span class="nv"&gt;CRITICAL_BATTERY_THRESHOLD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;90

&lt;span class="c"&gt;# File paths (relative to script directory)&lt;/span&gt;
&lt;span class="nv"&gt;AUDIO_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"notification.mp3"&lt;/span&gt;
&lt;span class="nv"&gt;LOG_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"battery_log.txt"&lt;/span&gt;

&lt;span class="c"&gt;# Notification settings&lt;/span&gt;
&lt;span class="nv"&gt;NOTIFICATION_TIMEOUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10
&lt;span class="nv"&gt;NOTIFICATION_WIDTH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;300
&lt;span class="nv"&gt;NOTIFICATION_HEIGHT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;100

&lt;span class="c"&gt;# System environment variables for GUI&lt;/span&gt;
&lt;span class="nv"&gt;DISPLAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;":0"&lt;/span&gt;
&lt;span class="nv"&gt;XAUTHORITY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/home/YOUR_USERNAME/.Xauthority"&lt;/span&gt;
&lt;span class="nv"&gt;XDG_RUNTIME_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/run/user/1000"&lt;/span&gt;
&lt;span class="nv"&gt;DBUS_SESSION_BUS_ADDRESS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"unix:path=/run/user/1000/bus"&lt;/span&gt;

&lt;span class="c"&gt;# Security (CHANGE THIS!)&lt;/span&gt;
&lt;span class="nv"&gt;SUDO_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"YOUR_SUDO_PASSWORD"&lt;/span&gt;

&lt;span class="c"&gt;# Battery path (usually BAT0, but might be BAT1 on some systems)&lt;/span&gt;
&lt;span class="nv"&gt;BATTERY_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/sys/class/power_supply/BAT0"&lt;/span&gt;

&lt;span class="c"&gt;# Logging&lt;/span&gt;
&lt;span class="nv"&gt;LOG_LEVEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"INFO"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🔐 Important Security Notes:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Replace &lt;code&gt;YOUR_USERNAME&lt;/code&gt;&lt;/strong&gt; with your actual username&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replace &lt;code&gt;YOUR_SUDO_PASSWORD&lt;/code&gt;&lt;/strong&gt; with your actual sudo password&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set restrictive permissions&lt;/strong&gt;: &lt;code&gt;chmod 600 battery_config.env&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  🔍 Finding Your Battery Path:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List available power supplies&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; /sys/class/power_supply/

&lt;span class="c"&gt;# Check if BAT0 exists, if not, use BAT1 or whatever you find&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /sys/class/power_supply/BAT&lt;span class="k"&gt;*&lt;/span&gt;/capacity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🎵 Getting a Notification Sound:
&lt;/h3&gt;

&lt;p&gt;You need an MP3 file named &lt;code&gt;notification.mp3&lt;/code&gt; in your project directory. You can:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Download a free notification sound&lt;/span&gt;
wget https://www.soundjay.com/misc/sounds/bell-ringing-05.mp3 &lt;span class="nt"&gt;-O&lt;/span&gt; notification.mp3

&lt;span class="c"&gt;# Or create a simple beep sound with ffmpeg&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;ffmpeg
ffmpeg &lt;span class="nt"&gt;-f&lt;/span&gt; lavfi &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"sine=frequency=1000:duration=1"&lt;/span&gt; notification.mp3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📝 Step 3: Create the Main Script
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;autobattoff.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# Get the directory where the script is located&lt;/span&gt;
&lt;span class="nv"&gt;script_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASH_SOURCE&lt;/span&gt;&lt;span class="p"&gt;[0]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Load configuration&lt;/span&gt;
&lt;span class="nv"&gt;config_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$script_dir&lt;/span&gt;&lt;span class="s2"&gt;/battery_config.env"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$config_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$config_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Configuration loaded from &lt;/span&gt;&lt;span class="nv"&gt;$config_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Configuration file &lt;/span&gt;&lt;span class="nv"&gt;$config_file&lt;/span&gt;&lt;span class="s2"&gt; not found!"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Set full file paths&lt;/span&gt;
&lt;span class="nv"&gt;audio_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$script_dir&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$AUDIO_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;log_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$script_dir&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$LOG_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;low_battery_threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$LOW_BATTERY_THRESHOLD&lt;/span&gt;
&lt;span class="nv"&gt;critical_low_battery_threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$CRITICAL_LOW_BATTERY_THRESHOLD&lt;/span&gt;
&lt;span class="nv"&gt;high_battery_threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HIGH_BATTERY_THRESHOLD&lt;/span&gt;
&lt;span class="nv"&gt;critical_battery_threshold&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$CRITICAL_BATTERY_THRESHOLD&lt;/span&gt;
&lt;span class="nv"&gt;sudo_password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SUDO_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Set environment variables from configuration&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DISPLAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DISPLAY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;XAUTHORITY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$XAUTHORITY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;XDG_RUNTIME_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$XDG_RUNTIME_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DBUS_SESSION_BUS_ADDRESS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DBUS_SESSION_BUS_ADDRESS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

log&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; - &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$log_file&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

log &lt;span class="s2"&gt;"Script started"&lt;/span&gt;

&lt;span class="c"&gt;# Check for required utilities&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; mpg123 &amp;amp;&amp;gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;log &lt;span class="s2"&gt;"mpg123 not found. Please install mpg123."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; yad &amp;amp;&amp;gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;log &lt;span class="s2"&gt;"yad not found. Please install yad."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;notify_user&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    log &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$message&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    mpg123 &lt;span class="nv"&gt;$audio_file&lt;/span&gt; &amp;amp;
    yad &lt;span class="nt"&gt;--title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$title&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$message&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--button&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;OK &lt;span class="nt"&gt;--width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$NOTIFICATION_WIDTH&lt;/span&gt; &lt;span class="nt"&gt;--height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$NOTIFICATION_HEIGHT&lt;/span&gt; &lt;span class="nt"&gt;--timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$NOTIFICATION_TIMEOUT&lt;/span&gt; &amp;amp;
    log &lt;span class="s2"&gt;"Notification sent: &lt;/span&gt;&lt;span class="nv"&gt;$message&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

check_battery_status&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;battery_level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BATTERY_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/capacity&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;charging_status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BATTERY_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/status&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$battery_level&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$charging_status&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;log &lt;span class="s2"&gt;"Error reading battery data."&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;1
    &lt;span class="k"&gt;fi
    &lt;/span&gt;log &lt;span class="s2"&gt;"Battery level: &lt;/span&gt;&lt;span class="nv"&gt;$battery_level&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    log &lt;span class="s2"&gt;"Charging status: &lt;/span&gt;&lt;span class="nv"&gt;$charging_status&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

check_battery_status

&lt;span class="c"&gt;# Main battery check logic&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$battery_level&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; &lt;span class="nv"&gt;$low_battery_threshold&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$charging_status&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"Charging"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;log &lt;span class="s2"&gt;"Critical battery level. Shutting down..."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$sudo_password&lt;/span&gt; | &lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-S&lt;/span&gt; /sbin/poweroff
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$battery_level&lt;/span&gt; &lt;span class="nt"&gt;-ge&lt;/span&gt; &lt;span class="nv"&gt;$low_battery_threshold&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$battery_level&lt;/span&gt; &lt;span class="nt"&gt;-le&lt;/span&gt; &lt;span class="nv"&gt;$critical_low_battery_threshold&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$charging_status&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"Charging"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;notify_user &lt;span class="s2"&gt;"URGENT: PLUG IN THE CHARGER!"&lt;/span&gt; &lt;span class="s2"&gt;"Charging Warning"&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$battery_level&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; &lt;span class="nv"&gt;$high_battery_threshold&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$battery_level&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; &lt;span class="nv"&gt;$critical_battery_threshold&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$charging_status&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"Charging"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;notify_user &lt;span class="s2"&gt;"Urgently disconnect the charger!"&lt;/span&gt; &lt;span class="s2"&gt;"Charging Warning"&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$battery_level&lt;/span&gt; &lt;span class="nt"&gt;-ge&lt;/span&gt; &lt;span class="nv"&gt;$critical_battery_threshold&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$charging_status&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"Charging"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;log &lt;span class="s2"&gt;"Battery at 90% or higher. Shutting down laptop..."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$sudo_password&lt;/span&gt; | &lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-S&lt;/span&gt; /sbin/poweroff
&lt;span class="k"&gt;else
    if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$charging_status&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"Charging"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;log &lt;span class="s2"&gt;"Laptop is charging."&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;log &lt;span class="s2"&gt;"Battery charge is normal."&lt;/span&gt;
    &lt;span class="k"&gt;fi
fi

&lt;/span&gt;log &lt;span class="s2"&gt;"Script completed"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔧 Step 4: Make Script Executable
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x autobattoff.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🧪 Step 5: Test the Script
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Manual Testing:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Test the script manually&lt;/span&gt;
./autobattoff.sh

&lt;span class="c"&gt;# Check the log file&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;battery_log.txt

&lt;span class="c"&gt;# Check current battery status manually&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /sys/class/power_supply/BAT0/capacity
&lt;span class="nb"&gt;cat&lt;/span&gt; /sys/class/power_supply/BAT0/status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test Different Scenarios:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test low battery&lt;/strong&gt;: Temporarily change &lt;code&gt;LOW_BATTERY_THRESHOLD&lt;/code&gt; to a value higher than your current battery level&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test high battery&lt;/strong&gt;: Plug in charger and temporarily change &lt;code&gt;HIGH_BATTERY_THRESHOLD&lt;/code&gt; to a value lower than current level&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test notifications&lt;/strong&gt;: Ensure you can see and hear notifications&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  ⏰ Step 6: Automate with Cron
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Edit your crontab:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Edit user crontab&lt;/span&gt;
crontab &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# Add this line to check battery every minute&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /bin/bash &lt;span class="s2"&gt;"/path/to/your/script/autobattoff.sh"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify Cron Job:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List current user's cron jobs&lt;/span&gt;
crontab &lt;span class="nt"&gt;-l&lt;/span&gt;

&lt;span class="c"&gt;# Monitor real-time log updates&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; battery_log.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📊 What the Script Logs
&lt;/h2&gt;

&lt;p&gt;The script creates a &lt;code&gt;battery_log.txt&lt;/code&gt; file with entries like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Script start/completion&lt;/li&gt;
&lt;li&gt;Current battery level and charging status&lt;/li&gt;
&lt;li&gt;Notifications sent&lt;/li&gt;
&lt;li&gt;Shutdown events&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can monitor the log with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# View recent logs&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt; battery_log.txt

&lt;span class="c"&gt;# Follow logs in real-time&lt;/span&gt;
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; battery_log.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🛡️ Security Note
&lt;/h2&gt;

&lt;p&gt;The script stores your sudo password in plain text in the configuration file. For better security:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set restrictive permissions: &lt;code&gt;chmod 600 battery_config.env&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Alternatively, you can modify &lt;code&gt;/etc/sudoers&lt;/code&gt; to allow passwordless shutdown:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;sudo &lt;/span&gt;visudo
   &lt;span class="c"&gt;# Add: YOUR_USERNAME ALL=(ALL) NOPASSWD: /sbin/poweroff&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🎯 Configuration Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Conservative thresholds&lt;/strong&gt;: Start with 40% for low and 80% for high&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adjust notification timeout&lt;/strong&gt;: Increase &lt;code&gt;NOTIFICATION_TIMEOUT&lt;/code&gt; if you need more time to see notifications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Battery path&lt;/strong&gt;: Make sure &lt;code&gt;BATTERY_PATH&lt;/code&gt; points to your actual battery (check &lt;code&gt;/sys/class/power_supply/&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🚀 Conclusion
&lt;/h2&gt;

&lt;p&gt;You now have a working battery monitoring system that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Prevents overcharging by shutting down at 90%&lt;/li&gt;
&lt;li&gt;✅ Prevents deep discharge by shutting down at 39%&lt;/li&gt;
&lt;li&gt;✅ Shows visual and audio notifications&lt;/li&gt;
&lt;li&gt;✅ Logs all activities for monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The script runs every minute via cron and automatically protects your battery health.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;🔗 Complete Repository&lt;/strong&gt;: &lt;a href="https://github.com/pefbrute/AutoBattOff" rel="noopener noreferrer"&gt;AutoBattOff on GitHub&lt;/a&gt; - Ready to download and use!&lt;/p&gt;

</description>
      <category>battery</category>
      <category>ubuntu</category>
      <category>bash</category>
      <category>linux</category>
    </item>
    <item>
      <title>Setting Up Tailwind CSS v4 in SvelteKit: The Vite Plugin Way (A Guide Based on Real Issues)</title>
      <dc:creator>Fedor Pasynkov</dc:creator>
      <pubDate>Fri, 09 May 2025 04:03:32 +0000</pubDate>
      <link>https://dev.to/fedor-pasynkov/setting-up-tailwind-css-v4-in-sveltekit-the-vite-plugin-way-a-guide-based-on-real-issues-380n</link>
      <guid>https://dev.to/fedor-pasynkov/setting-up-tailwind-css-v4-in-sveltekit-the-vite-plugin-way-a-guide-based-on-real-issues-380n</guid>
      <description>&lt;p&gt;&lt;em&gt;Published: 2025-05-11&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This guide shows you how to easily set up and integrate the latest &lt;strong&gt;Tailwind CSS v4&lt;/strong&gt; with your &lt;strong&gt;SvelteKit&lt;/strong&gt; project. We'll walk you through the process step-by-step, focusing on the official and recommended &lt;strong&gt;&lt;code&gt;@tailwindcss/vite&lt;/code&gt; plugin&lt;/strong&gt;, which simplifies the setup and avoids complex PostCSS configurations.&lt;/p&gt;

&lt;p&gt;Tailwind CSS v4 is currently in &lt;strong&gt;Alpha/RC (Release Candidate)&lt;/strong&gt; stage (as of 2025-05-11 - &lt;em&gt;please update this date&lt;/em&gt;). While this means you get to work with cutting-edge features, it also means some aspects might change before the stable release. This guide reflects the current state and best practices. &lt;strong&gt;Always refer to the &lt;a href="https://tailwindcss.com/docs" rel="noopener noreferrer"&gt;official Tailwind CSS documentation&lt;/a&gt; and the &lt;a href="https://github.com/tailwindlabs/tailwindcss/discussions" rel="noopener noreferrer"&gt;Tailwind CSS v4 release notes/discussions&lt;/a&gt; for the most up-to-date information.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We'll cover everything from initial project setup with SvelteKit's CLI to configuring Tailwind v4 and using it within your Svelte components.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important Compatibility Note:&lt;/strong&gt; Tailwind CSS v4 is designed for modern browsers: Safari 16.4+, Chrome 111+, Firefox 128+. If you need to support older browsers, please stick with v3.4 as v4 relies on modern CSS features like &lt;code&gt;@property&lt;/code&gt; and &lt;code&gt;color-mix()&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before you begin, ensure you have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Node.js and npm/yarn/pnpm:&lt;/strong&gt; Installed on your system. You can download Node.js from &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;nodejs.org&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;A SvelteKit Project:&lt;/strong&gt; You can create a new one using &lt;code&gt;npm create svelte@latest my-app&lt;/code&gt; or &lt;code&gt;npx sv create .&lt;/code&gt; as shown below. If you have an existing SvelteKit project, you can adapt the steps.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Package Manager:&lt;/strong&gt; Know which package manager you're using (npm, yarn, or pnpm).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1. Creating Your SvelteKit Project with Tailwind CSS Integration (via &lt;code&gt;npx sv create .&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;This section details how to &lt;strong&gt;add Tailwind CSS v4 to SvelteKit&lt;/strong&gt; during project initialization. The &lt;code&gt;npx sv create .&lt;/code&gt; CLI provides a streamlined way to scaffold a SvelteKit project and include Tailwind CSS during the initial setup.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Create your project directory:&lt;/strong&gt; Open your terminal, create the directory for your frontend application, and navigate into it:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;frontend
&lt;span class="nb"&gt;cd &lt;/span&gt;frontend
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Initialize the SvelteKit project using &lt;code&gt;sv&lt;/code&gt;:&lt;/strong&gt; Run the following command in the directory to start your &lt;strong&gt;SvelteKit Tailwind v4 integration&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx sv create &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Follow the interactive prompts:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Select your SvelteKit template.&lt;/li&gt;
&lt;li&gt;  Answer questions about TypeScript, ESLint, Prettier, and your adapter.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Crucially&lt;/strong&gt;, when asked "What would you like to add to your project?", use your arrow keys and spacebar to select the &lt;strong&gt;&lt;code&gt;tailwind&lt;/code&gt;&lt;/strong&gt; option.&lt;/li&gt;
&lt;li&gt;  Choose your package manager (e.g., &lt;strong&gt;&lt;code&gt;npm&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;yarn&lt;/code&gt;&lt;/strong&gt;, or &lt;strong&gt;&lt;code&gt;pnpm&lt;/code&gt;&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;  The CLI will install dependencies, including Tailwind CSS, and likely create some initial configuration files.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2. Verifying Installed Packages for a Smooth Tailwind v4 Setup in SvelteKit
&lt;/h3&gt;

&lt;p&gt;After &lt;code&gt;npx sv create .&lt;/code&gt; completes, let's ensure you have the correct packages installed for integrating Tailwind via the Vite plugin. This is key for a successful &lt;strong&gt;Tailwind v4 setup in SvelteKit&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Open &lt;code&gt;package.json&lt;/code&gt;:&lt;/strong&gt; Navigate to the root of your frontend directory (&lt;code&gt;./frontend/&lt;/code&gt;) and open the &lt;strong&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/strong&gt; file.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Check &lt;code&gt;devDependencies&lt;/code&gt;:&lt;/strong&gt; Confirm that the following packages are present (in v4-compatible versions, often &lt;code&gt;@latest&lt;/code&gt; or &lt;code&gt;@next&lt;/code&gt; for pre-releases):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;tailwindcss&lt;/code&gt;&lt;/strong&gt; (v4.x) - The core Tailwind CSS library.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;@tailwindcss/vite&lt;/code&gt;&lt;/strong&gt; - The official Vite plugin for Tailwind CSS. (Crucial for this guide!) You can find more about it on &lt;a href="https://www.npmjs.com/package/@tailwindcss/vite" rel="noopener noreferrer"&gt;npmjs.com&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;postcss&lt;/code&gt; (May be installed as a dependency of Vite or SvelteKit; might be needed for other PostCSS plugins, but not for Tailwind integration via the Vite plugin itself)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;autoprefixer&lt;/code&gt; (Not required for vendor prefixes in v4 when using the Vite plugin, but might be present if added by the CLI or for other purposes)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;@sveltejs/kit&lt;/code&gt; and &lt;code&gt;@sveltejs/vite-plugin-svelte&lt;/code&gt; (installed by the CLI, essential for SvelteKit projects. See &lt;a href="https://svelte.dev/docs/kit/introduction" rel="noopener noreferrer"&gt;SvelteKit docs&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Install or Uninstall as Needed:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# If @tailwindcss/vite is missing:&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; @tailwindcss/vite

&lt;span class="c"&gt;# If @tailwindcss/postcss was installed instead of @tailwindcss/vite:&lt;/span&gt;
&lt;span class="c"&gt;# npm uninstall -D @tailwindcss/postcss # Uninstall if you don't need the PostCSS plugin&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; @tailwindcss/vite

&lt;span class="c"&gt;# autoprefixer and postcss-import are NOT needed for Tailwind v4 integration via the Vite plugin.&lt;/span&gt;
&lt;span class="c"&gt;# You can uninstall them if they aren't required for other purposes:&lt;/span&gt;
&lt;span class="c"&gt;# npm uninstall -D autoprefixer postcss-import&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3. Step-by-Step: Configuring SvelteKit's Vite Setup for Tailwind CSS v4
&lt;/h3&gt;

&lt;p&gt;Since you're integrating Tailwind via the &lt;strong&gt;&lt;code&gt;@tailwindcss/vite&lt;/code&gt; plugin&lt;/strong&gt; and skipping the PostCSS config file approach, you'll configure Tailwind directly in your Vite configuration. This is a central part of the &lt;strong&gt;Tailwind v4 configuration in SvelteKit&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Open &lt;code&gt;vite.config.ts&lt;/code&gt; (or &lt;code&gt;.js&lt;/code&gt;):&lt;/strong&gt; This file is located in the root of your frontend directory.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Configure Vite Plugins:&lt;/strong&gt; Ensure the &lt;strong&gt;&lt;code&gt;tailwindcss()&lt;/code&gt;&lt;/strong&gt; plugin, imported from &lt;strong&gt;&lt;code&gt;@tailwindcss/vite&lt;/code&gt;&lt;/strong&gt;, is added to the root &lt;code&gt;plugins&lt;/code&gt; array of your Vite configuration:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sveltekit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@sveltejs/kit/vite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;tailwindcss&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@tailwindcss/vite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Import the Vite plugin&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;sveltekit&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nf"&gt;tailwindcss&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// Add the Tailwind CSS Vite plugin here!&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;css&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Make sure there is no 'postcss' section configured for Tailwind integration here.&lt;/span&gt;
    &lt;span class="c1"&gt;// If a 'postcss' section exists (e.g., added by the CLI or for other plugins like Autoprefixer),&lt;/span&gt;
    &lt;span class="c1"&gt;// ensure it does NOT include the Tailwind CSS plugin.&lt;/span&gt;
    &lt;span class="c1"&gt;// postcss: { plugins: [] },&lt;/span&gt;

    &lt;span class="c1"&gt;// A community recommendation (might help with Vite plugin issues):&lt;/span&gt;
    &lt;span class="c1"&gt;// transformer: 'lightningcss'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Optional: Configure CSS Transformer:&lt;/strong&gt; Some users report that setting &lt;code&gt;css.transformer: 'lightningcss'&lt;/code&gt; in &lt;code&gt;vite.config.ts&lt;/code&gt; can help resolve certain issues when using the &lt;code&gt;@tailwindcss/vite&lt;/code&gt; plugin. Add this line to the &lt;code&gt;css&lt;/code&gt; section if you encounter unexpected behavior:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;transformer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lightningcss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  4. Importing Tailwind CSS v4 in Your Main SvelteKit CSS File
&lt;/h3&gt;

&lt;p&gt;Locate the main CSS file that is imported somewhere in your SvelteKit application (e.g., &lt;strong&gt;&lt;code&gt;src/app.css&lt;/code&gt;&lt;/strong&gt;). This step is essential to &lt;strong&gt;add Tailwind CSS v4 to SvelteKit&lt;/strong&gt;'s styling pipeline.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use ONLY &lt;code&gt;@import "tailwindcss";&lt;/code&gt;:&lt;/strong&gt; According to the &lt;a href="https://tailwindcss.com/docs/installation/using-vite#step-3-import-tailwind-s-css" rel="noopener noreferrer"&gt;official Tailwind CSS v4 documentation&lt;/a&gt;, the correct and only recommended way to include Tailwind's base styles, components, and utilities is with a single &lt;code&gt;@import&lt;/code&gt; directive. &lt;strong&gt;The &lt;code&gt;@tailwind base;&lt;/code&gt;, &lt;code&gt;@tailwind components;&lt;/code&gt;, and &lt;code&gt;@tailwind utilities;&lt;/code&gt; directives have been removed in v4.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;"tailwindcss"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Remove any other old &lt;code&gt;@import&lt;/code&gt; or &lt;code&gt;@tailwind&lt;/code&gt; directives from this file.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  5. Mastering Tailwind CSS v4 Theme Configuration in SvelteKit
&lt;/h3&gt;

&lt;p&gt;In Tailwind CSS v4, the primary and recommended approach for theme configuration shifts to using CSS directly. Understanding this is key for your &lt;strong&gt;SvelteKit Tailwind v4 integration&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Configuration via &lt;code&gt;@theme&lt;/code&gt; in CSS (Recommended v4 Approach)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use &lt;code&gt;@theme&lt;/code&gt; in your main CSS file:&lt;/strong&gt; Define your custom theme properties directly in your main CSS file (e.g., &lt;code&gt;src/app.css&lt;/code&gt;) using the &lt;code&gt;@theme&lt;/code&gt; directive. These properties will become CSS variables and Tailwind theme tokens. With this approach, the &lt;code&gt;tailwind.config.js&lt;/code&gt; file is not needed for theme customization, only potentially for content paths.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;"tailwindcss"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;@theme&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* Define your custom theme properties as CSS variables */&lt;/span&gt;
  &lt;span class="nt"&gt;--color-brand-primary&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#3490&lt;/span&gt;&lt;span class="nt"&gt;dc&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="nt"&gt;--font-family-heading&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Georgia"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;serif&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="nt"&gt;--spacing-128&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;32&lt;/span&gt;&lt;span class="nt"&gt;rem&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* ... other theme variables */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Rest of your styles */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Automatic Content Detection and &lt;code&gt;@source&lt;/code&gt;:&lt;/strong&gt; Tailwind v4 automatically scans most project files. If you need to include files from non-standard locations (e.g., component libraries in &lt;code&gt;node_modules&lt;/code&gt;), use the &lt;code&gt;@source&lt;/code&gt; directive in the same CSS file:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;"tailwindcss"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@source&lt;/span&gt; &lt;span class="s1"&gt;"../node_modules/@my-company/ui-lib"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@theme&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;tailwind.config.js&lt;/code&gt; for Content (Optional):&lt;/strong&gt; The &lt;code&gt;tailwind.config.js&lt;/code&gt; file can still be used &lt;em&gt;solely&lt;/em&gt; for defining the &lt;code&gt;content&lt;/code&gt; section if you prefer explicitly listing paths or if auto-detection is insufficient:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** @type {import('tailwindcss').Config} */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/**/*.{html,js,svelte,ts}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// Explicitly list paths here&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// This section can be empty or removed if using @theme in CSS&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="c1"&gt;// corePlugins, safelist, separator options are NOT supported in v4 JS config&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;If you use &lt;code&gt;tailwind.config.js&lt;/code&gt; for content, you generally don't need the &lt;code&gt;@config&lt;/code&gt; directive in your CSS when using the Vite plugin and &lt;code&gt;@theme&lt;/code&gt; for theme definition.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. Effectively Using Tailwind CSS v4 in Svelte Components (&lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks)
&lt;/h3&gt;

&lt;p&gt;In Tailwind CSS v4, styles defined within Svelte component &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks (or CSS Modules) &lt;strong&gt;do not automatically have access&lt;/strong&gt; to theme variables, custom utilities, and variants defined in your main CSS file (&lt;code&gt;app.css&lt;/code&gt;). Here's how to handle it when you &lt;strong&gt;integrate Tailwind 4 with SvelteKit&lt;/strong&gt; components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use &lt;code&gt;@reference&lt;/code&gt; (for access to &lt;a class="mentioned-user" href="https://dev.to/apply"&gt;@apply&lt;/a&gt;, @utility, and &lt;a class="mentioned-user" href="https://dev.to/theme"&gt;@theme&lt;/a&gt; definitions):&lt;/strong&gt; To gain access to definitions made using &lt;code&gt;@apply&lt;/code&gt;, &lt;code&gt;@utility&lt;/code&gt;, or &lt;code&gt;@theme&lt;/code&gt; in your main CSS file, use the &lt;code&gt;@reference&lt;/code&gt; directive within the component's &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block, pointing to your main CSS file:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;@reference&lt;/span&gt; &lt;span class="s1"&gt;"../app.css"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Path to your main CSS file */&lt;/span&gt;

  &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;text-2xl&lt;/span&gt; &lt;span class="err"&gt;font-bold&lt;/span&gt; &lt;span class="err"&gt;text-blue-600;&lt;/span&gt; &lt;span class="c"&gt;/* @apply should now work */&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c"&gt;/* You can also use CSS variables here */&lt;/span&gt;
  &lt;span class="c"&gt;/* color: var(--color-blue-600); */&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello world!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use CSS Variables Directly (Preferred):&lt;/strong&gt; Often, instead of &lt;code&gt;@apply&lt;/code&gt;, you can directly use the corresponding Tailwind CSS variables in your &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks. This is the preferred approach as it's more performant and doesn't require &lt;code&gt;@reference&lt;/code&gt; for theme variables defined via &lt;code&gt;@theme&lt;/code&gt; in the main CSS:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight svelte"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;/* No @reference needed if only using theme variables */&lt;/span&gt;
  &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-blue-600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c"&gt;/* Access a color variable */&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--text-2xl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c"&gt;/* Access a text size variable */&lt;/span&gt;
    &lt;span class="c"&gt;/* ... other styles using variables */&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello world!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  7. Key General v4 Changes and Recommendations for SvelteKit Developers
&lt;/h3&gt;

&lt;p&gt;Be aware of these general changes in Tailwind v4 that might affect your &lt;strong&gt;SvelteKit Tailwind v4 integration&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Removed/Renamed Utilities:&lt;/strong&gt; Many deprecated utilities are removed, some are renamed (e.g., &lt;code&gt;outline-none&lt;/code&gt; to &lt;code&gt;outline-hidden&lt;/code&gt;), scales are changed (&lt;code&gt;shadow&lt;/code&gt;, &lt;code&gt;blur&lt;/code&gt;, &lt;code&gt;rounded&lt;/code&gt;), and default values are updated (&lt;code&gt;ring&lt;/code&gt; from 3px to 1px, border/ring colors to &lt;code&gt;currentColor&lt;/code&gt;). &lt;strong&gt;Always refer to the &lt;a href="https://tailwindcss.com/docs/upgrade-guide" rel="noopener noreferrer"&gt;official Tailwind CSS v4 upgrade guide&lt;/a&gt; for a complete list.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;space-x&lt;/code&gt;/&lt;code&gt;space-y&lt;/code&gt; Selector Change:&lt;/strong&gt; The selector used by these utilities has changed, which might require adjustments in some complex layouts. Consider using &lt;code&gt;flex&lt;/code&gt; or &lt;code&gt;grid&lt;/code&gt; with &lt;code&gt;gap&lt;/code&gt; as an alternative.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Custom Utilities (&lt;code&gt;@utility&lt;/code&gt;):&lt;/strong&gt; Use the &lt;code&gt;@utility&lt;/code&gt; directive instead of &lt;code&gt;@layer utilities&lt;/code&gt; and &lt;code&gt;@layer components&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@utility&lt;/span&gt; &lt;span class="n"&gt;my-custom-utility&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* Your styles */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Variables in Arbitrary Values:&lt;/strong&gt; The syntax for using CSS variables in arbitrary values changed from square brackets to parentheses: &lt;code&gt;bg-[--var]&lt;/code&gt; -&amp;gt; &lt;code&gt;bg-(--var)&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prefixes:&lt;/strong&gt; If you use a prefix, the syntax is now &lt;code&gt;prefix:class&lt;/code&gt; (e.g., &lt;code&gt;tw:flex&lt;/code&gt;). Configure it within the &lt;code&gt;@import&lt;/code&gt; rule: &lt;code&gt;@import "tailwindcss" prefix(tw);&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;theme()&lt;/code&gt;:&lt;/strong&gt; Using CSS variables directly (&lt;code&gt;var(--color-...)&lt;/code&gt;) is recommended. If you use &lt;code&gt;theme()&lt;/code&gt;, the syntax is now &lt;code&gt;theme(--variable-name)&lt;/code&gt; (e.g., &lt;code&gt;theme(--breakpoint-xl)&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Theme in JavaScript:&lt;/strong&gt; The &lt;code&gt;resolveConfig&lt;/code&gt; function is removed. Use &lt;code&gt;getComputedStyle&lt;/code&gt; to access theme CSS variables in JS.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Always consult the &lt;strong&gt;&lt;a href="https://tailwindcss.com/docs/installation/using-vite" rel="noopener noreferrer"&gt;official Tailwind CSS documentation&lt;/a&gt;&lt;/strong&gt; for the most current troubleshooting advice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion and Next Steps
&lt;/h2&gt;

&lt;p&gt;By following this guide, focusing on the &lt;strong&gt;&lt;code&gt;@tailwindcss/vite&lt;/code&gt; plugin integration&lt;/strong&gt;, you should be able to successfully set up and &lt;strong&gt;integrate Tailwind CSS v4 with your SvelteKit project&lt;/strong&gt;. This approach aligns with the latest v4 practices, helping you avoid potential issues related to older PostCSS configurations.&lt;/p&gt;

&lt;p&gt;Remember that Tailwind CSS v4 is an evolving tool. Keep an eye on the &lt;a href="https://tailwindcss.com/blog" rel="noopener noreferrer"&gt;official Tailwind CSS blog&lt;/a&gt; and documentation for updates and new features.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;I'd love to hear from you!&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Did you find this guide helpful for your &lt;strong&gt;SvelteKit Tailwind v4 setup&lt;/strong&gt;?&lt;/li&gt;
&lt;li&gt;  Are there any other aspects of Tailwind v4 or SvelteKit integration you'd like to see covered?&lt;/li&gt;
&lt;li&gt;  Encountered any specific issues or found other solutions?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please &lt;strong&gt;leave a comment below&lt;/strong&gt; to share your experience, ask questions, or suggest improvements! Your feedback helps make this guide better for everyone.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>sveltekit</category>
      <category>tailwindcss</category>
      <category>vite</category>
      <category>css</category>
    </item>
    <item>
      <title>Twitch OAuth2 Explained for twitchio Bots: Getting Authorized</title>
      <dc:creator>Fedor Pasynkov</dc:creator>
      <pubDate>Wed, 07 May 2025 01:54:31 +0000</pubDate>
      <link>https://dev.to/fedor-pasynkov/unlock-your-twitch-bots-potential-the-ultimate-oauth2-guide-22he</link>
      <guid>https://dev.to/fedor-pasynkov/unlock-your-twitch-bots-potential-the-ultimate-oauth2-guide-22he</guid>
      <description>&lt;p&gt;Hey developers! If you're looking to build a Twitch bot that can interact with chat, perform moderation actions, or access user data, especially using the powerful &lt;code&gt;twitchio&lt;/code&gt; library, you'll need to navigate the world of Twitch OAuth2. It might seem daunting at first, but this guide will walk you through every step, making the process clear and manageable, so you can get your &lt;code&gt;twitchio&lt;/code&gt; bot up and running.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is OAuth 2.0 and Why Do You Need It?
&lt;/h2&gt;

&lt;p&gt;OAuth 2.0 is the industry-standard protocol for authorization. It allows applications (like your &lt;code&gt;twitchio&lt;/code&gt; bot) to access user resources on a service (Twitch, in this case) without needing the user's login credentials directly. Instead, it uses &lt;strong&gt;access tokens&lt;/strong&gt;. All the steps described here are essential for your &lt;code&gt;twitchio&lt;/code&gt; bot to authenticate and interact with Twitch services.&lt;/p&gt;

&lt;p&gt;Here's a quick rundown of the key components you'll be working with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Access Token&lt;/strong&gt;: A short-lived credential used to authenticate API requests to Twitch on behalf of a user (your bot). Your &lt;code&gt;twitchio&lt;/code&gt; bot will use this token for most of its API calls.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Refresh Token&lt;/strong&gt;: A long-lived credential used to obtain a new Access Token when the current one expires, without requiring the user to go through the authorization process again. &lt;code&gt;twitchio&lt;/code&gt; can be configured to handle token refreshing automatically if you provide it with this token.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Client ID&lt;/strong&gt;: A public identifier for your application, which you get when you register your app on Twitch. This is fundamental for any Twitch API interaction, including those made by &lt;code&gt;twitchio&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Client Secret&lt;/strong&gt;: A confidential key known only to your application and Twitch. &lt;strong&gt;Treat this like a password!&lt;/strong&gt; It's used in server-to-server communication, like exchanging an authorization code for tokens, or refreshing tokens, all of which are vital for your &lt;code&gt;twitchio&lt;/code&gt; bot's long-term operation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end of this guide, you'll have all these tokens and understand how to use them to power up your Twitch bot, specifically one built with &lt;code&gt;twitchio&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; Prerequisites
&lt;/li&gt;
&lt;li&gt; Step 1: Registering Your Application on the Twitch Developer Portal
&lt;/li&gt;
&lt;li&gt; Step 2: Defining Necessary Permissions (Scopes)
&lt;/li&gt;
&lt;li&gt; Step 3: Preparing for Authorization
&lt;/li&gt;
&lt;li&gt; Step 4: Constructing the Authorization URL
&lt;/li&gt;
&lt;li&gt; Step 5: Authorizing the Application and Obtaining the Code
&lt;/li&gt;
&lt;li&gt; Step 6: Exchanging the Authorization Code for Tokens
&lt;/li&gt;
&lt;li&gt; Step 7: Storing Your Tokens Securely
&lt;/li&gt;
&lt;li&gt; Step 8: Validating Your Access Token (Recommended)
&lt;/li&gt;
&lt;li&gt;Step 9: Refreshing the Access Token&lt;/li&gt;
&lt;li&gt;Wrapping Up and Next Steps&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before you dive in, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;A Twitch Account for Your Bot&lt;/strong&gt;: Create a separate Twitch account that your bot will use. &lt;strong&gt;Do not use your main Twitch account for the bot.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;A Twitch Account for Your Channel&lt;/strong&gt;: The channel your bot will interact with or moderate.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Registering Your Application on the Twitch Developer Portal
&lt;/h2&gt;

&lt;p&gt;To interact with the Twitch API, your bot needs to be registered as an application.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Go to the &lt;a href="https://dev.twitch.tv/console/apps" rel="noopener noreferrer"&gt;Twitch Developer Console&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt; Log in using your &lt;strong&gt;main Twitch account&lt;/strong&gt; (or your developer account). &lt;strong&gt;Do not use the bot's account at this stage.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; Click the &lt;strong&gt;"+ Register Your Application"&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt; Fill out the form:

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Name&lt;/strong&gt;: Choose any name for your application (e.g., "MyAwesomeBot"). &lt;strong&gt;IMPORTANT&lt;/strong&gt;: Do not use the word "Twitch" in the name.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;OAuth Redirect URLs&lt;/strong&gt;: Enter &lt;code&gt;http://localhost:3000&lt;/code&gt;. This URL is used during the authorization process. Even if you don't have a server running at this address, it's required to obtain the authorization code.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Category&lt;/strong&gt;: Select "Chat Bot".&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;"Create"&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Getting Your Client ID and Client Secret
&lt;/h3&gt;

&lt;p&gt;Once your application is created, you'll see your &lt;strong&gt;Client ID&lt;/strong&gt;. Copy it and save it somewhere safe.&lt;/p&gt;

&lt;p&gt;To get your &lt;strong&gt;Client Secret&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; On your application's page, click the &lt;strong&gt;"New Secret"&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt; Twitch will generate and display the &lt;strong&gt;Client Secret&lt;/strong&gt;. &lt;strong&gt;IMPORTANT&lt;/strong&gt;: The Client Secret is shown only once. Copy it immediately and store it securely with your Client ID. If you lose it, you'll have to generate a new one.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These credentials are vital for your bot to authenticate with the Twitch API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Defining Necessary Permissions (Scopes)
&lt;/h2&gt;

&lt;p&gt;Scopes define what actions your bot can perform on behalf of the user (the bot's account). For a typical moderation bot, you might need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;chat:read&lt;/code&gt; - Read chat messages.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;chat:edit&lt;/code&gt; - Send chat messages (and delete messages via &lt;code&gt;/delete&lt;/code&gt; command).&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;channel:moderate&lt;/code&gt; - Use moderation commands (e.g., &lt;code&gt;/timeout&lt;/code&gt;, &lt;code&gt;/ban&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;moderator:manage:chat_messages&lt;/code&gt; - Allows deleting messages directly via API (if you plan to use this method).&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;moderator:manage:banned_users&lt;/code&gt; - Manage banned users.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;moderator:read:followers&lt;/code&gt; - Read the list of followers (if your bot needs this).&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;whispers:read&lt;/code&gt; - Read whispers.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;whispers:edit&lt;/code&gt; - Send whispers.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;user:manage:whispers&lt;/code&gt; - Manage whispers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Find the full list of scopes and their descriptions in the &lt;a href="https://dev.twitch.tv/docs/authentication/scopes/" rel="noopener noreferrer"&gt;official Twitch documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For example, your scopes string might look like this:&lt;br&gt;
&lt;code&gt;chat:read+chat:edit+channel:moderate+moderator:manage:chat_messages&lt;/code&gt;&lt;br&gt;
(The &lt;code&gt;+&lt;/code&gt; sign is used as a separator when forming the URL, or a space if used in another context).&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 3: Preparing for Authorization
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Log out of your main Twitch account&lt;/strong&gt; in all your browser tabs. This is crucial to avoid accidentally authorizing the app with your main account.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Log into the Twitch account your bot will use&lt;/strong&gt; (the bot's account).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;(Recommended)&lt;/strong&gt; Make the bot's account a moderator on your main channel. This expands its capabilities (e.g., higher message rate limits, ability to execute moderation commands). In your main channel's chat, type:
&lt;code&gt;/mod YOUR_BOT_ACCOUNT_NAME&lt;/code&gt;
Replace &lt;code&gt;YOUR_BOT_ACCOUNT_NAME&lt;/code&gt; with your bot's actual username.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Step 4: Constructing the Authorization URL
&lt;/h2&gt;

&lt;p&gt;Now, you need to create a special URL. You'll open this URL in the browser where your bot's account is logged in to authorize your application.&lt;/p&gt;

&lt;p&gt;The URL structure is:&lt;br&gt;
&lt;code&gt;https://id.twitch.tv/oauth2/authorize?response_type=code&amp;amp;client_id=YOUR_CLIENT_ID&amp;amp;redirect_uri=http://localhost:3000&amp;amp;scope=YOUR_SCOPES&amp;amp;force_verify=true&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Replace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;YOUR_CLIENT_ID&lt;/code&gt;: With your Client ID from Step 1.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;YOUR_SCOPES&lt;/code&gt;: With the scopes string defined in Step 2 (e.g., &lt;code&gt;chat:read+chat:edit+channel:moderate&lt;/code&gt;). Ensure scopes are separated by &lt;code&gt;+&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example of a complete URL:&lt;br&gt;
&lt;code&gt;https://id.twitch.tv/oauth2/authorize?response_type=code&amp;amp;client_id=abcdef1234567890&amp;amp;redirect_uri=http://localhost:3000&amp;amp;scope=chat:read+chat:edit+channel:moderate&amp;amp;force_verify=true&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;force_verify=true&lt;/code&gt; parameter prompts for explicit authorization, even if the user has previously granted permission.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 5: Authorizing the Application and Obtaining the Code
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; Copy the URL you constructed in Step 4.&lt;/li&gt;
&lt;li&gt; Paste it into the address bar of the browser where you are &lt;strong&gt;logged in as the bot&lt;/strong&gt;. Hit Enter.&lt;/li&gt;
&lt;li&gt; Twitch will ask you to authorize your application to access data according to the specified scopes. Carefully review the requested permissions and click &lt;strong&gt;"Authorize"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; After successful authorization, the browser will redirect you to the &lt;code&gt;redirect_uri&lt;/code&gt; (&lt;code&gt;http://localhost:3000&lt;/code&gt;). Since you likely don't have a web server listening there, you'll see an error like "This site can't be reached." &lt;strong&gt;This is normal and expected!&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The important part&lt;/strong&gt;: Look at the browser's address bar. The URL will look something like this:
&lt;code&gt;http://localhost:3000/?code=LONG_AUTHORIZATION_CODE&amp;amp;scope=requested_scopes&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Copy the value of the &lt;code&gt;code&lt;/code&gt; parameter from this URL. This is your &lt;strong&gt;Authorization Code&lt;/strong&gt;. It's single-use and has a short lifespan.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example: If the URL is &lt;code&gt;http://localhost:3000/?code=zyxw9876543210&amp;amp;scope=chat:read+chat:edit&lt;/code&gt;, your authorization code is &lt;code&gt;zyxw9876543210&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 6: Exchanging the Authorization Code for Tokens (Access &amp;amp; Refresh)
&lt;/h2&gt;

&lt;p&gt;The Authorization Code you just got needs to be exchanged for an Access Token and a Refresh Token. This exchange happens by sending a POST request to the Twitch server. &lt;strong&gt;This request must include your Client Secret, so it cannot be done from client-side JavaScript in a browser.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Using &lt;code&gt;curl&lt;/code&gt; to Exchange the Code for Tokens
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;curl&lt;/code&gt; is a powerful command-line tool for making HTTP requests, available on most operating systems.&lt;/p&gt;

&lt;p&gt;Open your terminal or command prompt and execute the following command, substituting your actual values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://id.twitch.tv/oauth2/token"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/x-www-form-urlencoded"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"client_id=YOUR_CLIENT_ID"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"client_secret=YOUR_CLIENT_SECRET"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"code=YOUR_AUTHORIZATION_CODE"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"grant_type=authorization_code"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"redirect_uri=http://localhost:3000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;YOUR_CLIENT_ID&lt;/code&gt;: Your Client ID.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;YOUR_CLIENT_SECRET&lt;/code&gt;: Your Client Secret.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;YOUR_AUTHORIZATION_CODE&lt;/code&gt;: The Authorization Code obtained in Step 5.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example JSON response from Twitch:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NEW_ACCESS_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"refresh_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NEW_REFRESH_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expires_in"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14058&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scope"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"chat:read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"chat:edit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"channel:moderate"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"token_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bearer"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From this response, you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;access_token&lt;/code&gt;: Your Access Token.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;refresh_token&lt;/code&gt;: Your Refresh Token.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Copy these and store them securely. &lt;code&gt;expires_in&lt;/code&gt; indicates the Access Token's lifetime in seconds.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; If you prefer a GUI, tools like Postman or Insomnia can also be used to make this POST request.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 7: Storing Your Tokens Securely
&lt;/h2&gt;

&lt;p&gt;You now have all the essential credentials for your bot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;CLIENT_ID&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;CLIENT_SECRET&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;ACCESS_TOKEN&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;REFRESH_TOKEN&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;BOT_USERNAME&lt;/code&gt; (the bot account's username)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;CHANNEL_NAME&lt;/code&gt; (the channel name where the bot will operate)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's crucial to store these securely, especially &lt;code&gt;CLIENT_SECRET&lt;/code&gt;, &lt;code&gt;ACCESS_TOKEN&lt;/code&gt;, and &lt;code&gt;REFRESH_TOKEN&lt;/code&gt;. These are the keys your &lt;code&gt;twitchio&lt;/code&gt; bot will use to authenticate and function.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended storage methods:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Environment Variables&lt;/strong&gt;: The most secure method for production. Your &lt;code&gt;twitchio&lt;/code&gt; application will read these variables at startup.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;.env&lt;/code&gt; file&lt;/strong&gt;: Create a file named &lt;code&gt;.env&lt;/code&gt; in your project's root directory and add key-value pairs. &lt;strong&gt;Always add &lt;code&gt;.env&lt;/code&gt; to your &lt;code&gt;.gitignore&lt;/code&gt; file&lt;/strong&gt; to prevent accidentally committing it to your repository. Your &lt;code&gt;twitchio&lt;/code&gt; bot can load these using a library like &lt;code&gt;python-dotenv&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example &lt;code&gt;.env&lt;/code&gt; file content (often used for &lt;code&gt;twitchio&lt;/code&gt; bots):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BOT_USERNAME=your_bot_name
CHANNEL_NAME=your_channel_name
CLIENT_ID=your_client_id
CLIENT_SECRET=your_client_secret
ACCESS_TOKEN=your_access_token
REFRESH_TOKEN=your_refresh_token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 8: Validating Your Access Token (Recommended)
&lt;/h2&gt;

&lt;p&gt;To ensure your Access Token is valid and belongs to the correct user (your bot), you can make a validation request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request (GET):&lt;/strong&gt;&lt;br&gt;
URL: &lt;code&gt;https://id.twitch.tv/oauth2/validate&lt;/code&gt;&lt;br&gt;
Headers: &lt;code&gt;Authorization: Bearer YOUR_ACCESS_TOKEN&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;curl&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET &lt;span class="s2"&gt;"https://id.twitch.tv/oauth2/validate"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer YOUR_ACCESS_TOKEN"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;YOUR_ACCESS_TOKEN&lt;/code&gt; with the Access Token you obtained.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example of a successful JSON response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"client_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your_client_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"login"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your_bot_name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scopes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"chat:read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chat:edit"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bot_user_id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expires_in"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13900&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Remaining&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;lifetime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;seconds&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify that &lt;code&gt;login&lt;/code&gt; matches your bot's username and &lt;code&gt;client_id&lt;/code&gt; matches your application's Client ID.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 9: Refreshing the Access Token
&lt;/h2&gt;

&lt;p&gt;Access Tokens have a limited lifespan (usually a few hours). When it expires, your bot will lose API access. To prevent this, use the Refresh Token to get a new Access Token.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request (POST):&lt;/strong&gt;&lt;br&gt;
URL: &lt;code&gt;https://id.twitch.tv/oauth2/token&lt;/code&gt;&lt;br&gt;
Body (x-www-form-urlencoded):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;grant_type&lt;/code&gt;: &lt;code&gt;refresh_token&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;refresh_token&lt;/code&gt;: YOUR_REFRESH_TOKEN&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;client_id&lt;/code&gt;: YOUR_CLIENT_ID&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;client_secret&lt;/code&gt;: YOUR_CLIENT_SECRET&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;curl&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://id.twitch.tv/oauth2/token"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/x-www-form-urlencoded"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"grant_type=refresh_token"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"refresh_token=YOUR_REFRESH_TOKEN"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"client_id=YOUR_CLIENT_ID"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"client_secret=YOUR_CLIENT_SECRET"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;YOUR_REFRESH_TOKEN&lt;/code&gt;, &lt;code&gt;YOUR_CLIENT_ID&lt;/code&gt;, and &lt;code&gt;YOUR_CLIENT_SECRET&lt;/code&gt; with your values.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server Response:&lt;/strong&gt;&lt;br&gt;
The server will return a new &lt;code&gt;access_token&lt;/code&gt; and possibly a new &lt;code&gt;refresh_token&lt;/code&gt;. &lt;strong&gt;Always save the new &lt;code&gt;refresh_token&lt;/code&gt; if one is provided, as the old one might become invalid.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LATEST_ACCESS_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"refresh_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"POSSIBLY_NEW_REFRESH_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scope"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"chat:read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chat:edit"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"token_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bearer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expires_in"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14400&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update your stored &lt;code&gt;ACCESS_TOKEN&lt;/code&gt; and &lt;code&gt;REFRESH_TOKEN&lt;/code&gt; with these new values. Your bot should automate this process before the Access Token expires.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up and Next Steps
&lt;/h2&gt;

&lt;p&gt;Congratulations! You've successfully navigated the Twitch OAuth2 process and obtained the necessary tokens. Your bot is now ready for action.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaways:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Security First&lt;/strong&gt;: Always keep your &lt;code&gt;Client Secret&lt;/code&gt;, &lt;code&gt;Access Token&lt;/code&gt;, and &lt;code&gt;Refresh Token&lt;/code&gt; secure. Never expose them in client-side code or commit them to public repositories.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Token Lifecycle&lt;/strong&gt;: Understand that Access Tokens expire and need to be refreshed using the Refresh Token. Implement logic in your bot to handle this automatically.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Scopes Matter&lt;/strong&gt;: Only request the permissions (scopes) your bot absolutely needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stay tuned for the next article in this series, where we'll dive into the actual implementation of a Twitch bot using the tokens and knowledge you've gained here!&lt;/p&gt;

&lt;p&gt;Now that you have the keys to the kingdom, what cool features are you planning to build for your Twitch bot? Share your ideas or any questions you have in the comments below! Happy coding!&lt;/p&gt;

</description>
      <category>twitch</category>
      <category>oauth</category>
      <category>bots</category>
      <category>twitchio</category>
    </item>
    <item>
      <title>🎬 Movie Magic with Python: Your First Steps into IMDb Data using imdbpy</title>
      <dc:creator>Fedor Pasynkov</dc:creator>
      <pubDate>Tue, 06 May 2025 23:12:17 +0000</pubDate>
      <link>https://dev.to/fedor-pasynkov/movie-magic-with-python-your-first-steps-into-imdb-data-using-imdbpy-4aoc</link>
      <guid>https://dev.to/fedor-pasynkov/movie-magic-with-python-your-first-steps-into-imdb-data-using-imdbpy-4aoc</guid>
      <description>&lt;p&gt;Ever wondered how to programmatically access the treasure trove of information on IMDb? Whether you're building a movie recommendation engine, a film data analysis tool, or just curious about your favorite series, the &lt;code&gt;imdbpy&lt;/code&gt; library for Python is your gateway!&lt;/p&gt;

&lt;p&gt;In this guide, we'll walk through how to install &lt;code&gt;imdbpy&lt;/code&gt;, search for movies and TV shows, fetch detailed information, and navigate the rich data structures returned. We'll use the iconic series "Breaking Bad" as our running example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who is this for?
&lt;/h2&gt;

&lt;p&gt;This article is for Python developers of all levels who want to integrate IMDb data into their applications or scripts. If you love movies and code, you're in the right place!&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠️ Setting Up: Installation and Initialization
&lt;/h2&gt;

&lt;p&gt;First things first, let's get &lt;code&gt;imdbpy&lt;/code&gt; installed. Open your terminal and type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;imdbpy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, you can import it into your Python script and create an instance of the &lt;code&gt;IMDb&lt;/code&gt; class. This object will be your primary tool for interacting with IMDb.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;imdb&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;IMDb&lt;/span&gt;

&lt;span class="c1"&gt;# Create an instance of the IMDb class
&lt;/span&gt;&lt;span class="n"&gt;ia&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;IMDb&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;IMDb instance created!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By the way, you can find a complete working code example, which you can run and test, at the following link: &lt;a href="https://github.com/pefbrute/imdbpy_example/blob/main/src/internet-movie-database.py" rel="noopener noreferrer"&gt;working example&lt;/a&gt;. This will help you quickly understand how to use the library.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔎 Searching for Titles
&lt;/h2&gt;

&lt;p&gt;Let's say we want to find "Breaking Bad". The &lt;code&gt;search_movie()&lt;/code&gt; method is what we need. It returns a list of &lt;code&gt;Movie&lt;/code&gt; objects, which are lightweight summaries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;search_term&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Breaking Bad&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="c1"&gt;# You can limit the number of results with the 'results' parameter
&lt;/span&gt;    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search_movie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;search_term&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Let's get the top result
&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Found &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; result(s) for &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;search_term&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;movie&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Each 'movie' object here has basic info like title, year, and IMDb ID
&lt;/span&gt;            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;- Title: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  Year: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  IMDb ID: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getID&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;# We'll need this ID for the next step!
&lt;/span&gt;    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No results found for &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;search_term&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;An error occurred during search: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll notice we get the title, year, and most importantly, the &lt;code&gt;imdbID&lt;/code&gt;. For "Breaking Bad", this is typically &lt;code&gt;'0903747'&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎬 Getting the Full Scoop: Fetching Detailed Information
&lt;/h2&gt;

&lt;p&gt;The initial search gives us basic info. To get all the juicy details – cast, plot, ratings, genres, cover images, and more – we use the &lt;code&gt;get_movie()&lt;/code&gt; method with the &lt;code&gt;movieID&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Assuming 'results' contains our search result and 'movie' is the first item
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;first_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;movie_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;first_result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getID&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# e.g., '0903747' for Breaking Bad
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Fetching full details for &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;first_result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; (ID: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;movie_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# This is the magic call! It fetches a wealth of data.
&lt;/span&gt;        &lt;span class="n"&gt;full_movie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_movie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;movie_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;full_movie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Successfully fetched details for: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;full_movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;# Now, 'full_movie' is a rich Movie object.
&lt;/span&gt;            &lt;span class="c1"&gt;# Let's explore what we can get!
&lt;/span&gt;        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Could not fetch details for ID &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;movie_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;An error occurred fetching details for ID &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;movie_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No search results to fetch details from.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📊 Accessing the Data: What's Inside?
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;full_movie&lt;/code&gt; object now holds a comprehensive dataset. You can access its information like a Python dictionary or using the &lt;code&gt;.get()&lt;/code&gt; method (which is safer as it allows default values if a key is missing).&lt;/p&gt;

&lt;p&gt;Let's pull out some specific details for "Breaking Bad":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pprint&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pprint&lt;/span&gt; &lt;span class="c1"&gt;# For nicely printing complex data structures
&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;full_movie&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;full_movie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# Check if full_movie exists and is not None
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; Detailed Info for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;full_movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Localized Title: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;full_movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;localized title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Kind: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;full_movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;kind&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# e.g., 'tv series', 'movie'
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Year: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;full_movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;full_movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;kind&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tv series&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Series Years: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;full_movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;series years&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# e.g., '2008-2013'
&lt;/span&gt;        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Number of Seasons: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;full_movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;number of seasons&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Cover URL: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;full_movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cover url&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# You could even try to display this image in a rich environment!
&lt;/span&gt;    &lt;span class="c1"&gt;# Example: https://m.media-amazon.com/images/M/MV5BMzU5ZGYzNmQtMTdhYy00OGRiLTg0NmQtYjVjNzliZTg1ZGE4XkEyXkFqcGc@._V1_SX101_CR0,0,101,150_.jpg
&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Rating: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;full_movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rating&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (based on &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;full_movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;votes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; votes)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Genres:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;genres&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;full_movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;genres&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;genres&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;genre&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;genres&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  - &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;genre&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# e.g., ['Crime', 'Drama', 'Thriller']
&lt;/span&gt;    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  N/A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Plot Outline:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Plot can be a list, let's take the first one if available
&lt;/span&gt;    &lt;span class="n"&gt;plot_outline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;full_movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;plot outline&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plot_outline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="c1"&gt;# Sometimes plot is a list of strings
&lt;/span&gt;        &lt;span class="n"&gt;plot_outline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;plot_outline&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;plot_outline&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plot_outline&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;plot_outline&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Print a snippet
&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Cast (Top 5):&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;full_movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cast&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="c1"&gt;# 'person' is a Person object. Access its 'name' attribute.
&lt;/span&gt;        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  - &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="c1"&gt;# Example: Bryan Cranston, Anna Gunn, Aaron Paul ...
&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Production Companies (First 3):&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;companies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;full_movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;production companies&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;company&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;companies&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="c1"&gt;# 'company' is a Company object. Access its 'name' attribute.
&lt;/span&gt;        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  - &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Example: High Bridge Productions, Gran Via Productions, Sony Pictures Television
&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Want to see EVERYTHING? The raw data is in `full_movie.data`
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Full Raw Data Example (all available fields for Breaking Bad) ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# This is the complete data structure you can expect for a detailed movie/series.
&lt;/span&gt;    &lt;span class="c1"&gt;# Note: In a real script, you'd access specific keys rather than printing everything.
&lt;/span&gt;    &lt;span class="c1"&gt;# Below is an example of what `full_movie.data` would contain for "Breaking Bad":
&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;akas&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Rompiendo Tus Límites Morales (Spain)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;绝命毒师 (China, Mandarin title)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ブレイキング・バッド (Japan, Japanese title)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Kriva pota (Slovenia)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Во все тяжкие (Russia)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;aspect ratio&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1.78 : 1 (UHD)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cast&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0186505[http] name:_Bryan Cranston_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0348152[http] name:_Anna Gunn_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0666739[http] name:_Aaron Paul_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:1336827[http] name:_Betsy Brandt_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:2666409[http] name:_RJ Mitte_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0606487[http] name:_Dean Norris_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0644022[http] name:_Bob Odenkirk_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:2366374[http] name:_Steven Michael Quezada_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0052186[http] name:_Jonathan Banks_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0002064[http] name:_Giancarlo Esposito_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:1889973[http] name:_Charles Baker_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0687146[http] name:_Jesse Plemons_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0184086[http] name:_Christopher Cousins_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0292182[http] name:_Laura Fraser_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:2804503[http] name:_Matt Jones_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0928795[http] name:_Michael Shamus Wiles_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:1089417[http] name:_Lavell Crawford_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:1530474[http] name:_Ray Campbell_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:1269983[http] name:_Krysten Ritter_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0784831[http] name:_Carmen Serano_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:1986960[http] name:_Emily Rios_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0662669[http] name:_Tina Parker_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0546797[http] name:_Mark Margolis_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0084497[http] name:_Jeremiah Bitsui_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:3970678[http] name:_Ian Posada_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0182345[http] name:_David Costabile_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0100889[http] name:_Michael Bowen_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:1450145[http] name:_David House_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0710271[http] name:_Kevin Rankin_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:3718379[http] name:_Daniel Moncada_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:4577760[http] name:_Patrick Sane_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:5987281[http] name:_Moira Bryg MacDonald_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0372961[http] name:_Jessica Hecht_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0122987[http] name:_Bill Burr_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:2256387[http] name:_Marius Stan_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:2968788[http] name:_Rodney Rush_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0856086[http] name:_Todd Terry_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:1616991[http] name:_Luis Moncada_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:3979839[http] name:_Kaija Bales_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:1324884[http] name:_Tait Fletcher_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:5597998[http] name:_Matthew T. Metzler_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0190441[http] name:_Raymond Cruz_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0002128[http] name:_Tess Harper_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0209496[http] name:_John de Lancie_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0122716[http] name:_Jere Burns_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0316708[http] name:_Nigel Gibbs_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0452622[http] name:_Tom Kiesche_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0173997[http] name:_Maurice Compte_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:2813734[http] name:_Morse Bicknell_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:5518772[http] name:_Christopher King_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;certificates&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Argentina:18&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Australia:MA15+&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Brazil:16&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Brazil:14::(some episodes)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Bulgaria:D&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Canada:14A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Canada:13+::(Québec)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Canada:14+::(TV Rating)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Chile:18&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Colombia:15&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Denmark:15::(DVD and Blu-ray rating)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Ecuador:16::(self-applied)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Egypt:18+::(self-applied)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Finland:K-16&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;France:Tous publics::(with warning)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Germany:16&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hong Kong:IIB&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hong Kong:16+::(self-applied)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hungary:18&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Iceland:16&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;India:18::(self-applied)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Indonesia:21+&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Ireland:18::(some episodes, Season 1-2, 4-5 DVD)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Ireland:15::(some episodes, Season 3, 6 DVD)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Israel:16::(self-applied)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Italy:VM14::(self-applied)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Japan:R18+&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Lithuania:N-16::(Netflix self-rating)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Mexico:C&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Netherlands:16&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;New Zealand:R16::(most episodes, Season 1-3, 5-6 DVD)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;New Zealand:M::(one episode)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;New Zealand:R13::(some episodes)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;New Zealand:R18::(some episodes, Season 4 DVD)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Norway:15::(DVD and Blu-ray rating)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Peru:18&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Philippines:R-13::(Season 1-5, Part 1)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Philippines:R-16::(Season 5, Part 2)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Philippines:18+::(self-applied)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Poland:12&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Portugal:M/16&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Portugal:16&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Russia:18+&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Singapore:M18&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Singapore:NC16::(final season)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;South Africa:16&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;South Korea:19&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Spain:16&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Sweden:15::(DVD and Blu-ray rating)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Taiwan:15+&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Thailand:20::(seasons 1-3)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Thailand:15::(seasons 4-5)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Turkey:18+::(self-applied)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;United Kingdom:18&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;United Kingdom:15::(most episodes, Season 2-3, 5-6 DVD)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;United Kingdom:12::(one episode)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;United Kingdom:18::(some episodes, Season 1, 4 DVD)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;United States:TV-MA&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;United States:TV-MA&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;United States:TV-14::(some episodes)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Ukraine:18&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;United Arab Emirates:18+::(self-applied)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Vietnam:C18&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;color info&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Color&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;countries&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;United States&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;country codes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;us&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cover url&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://m.media-amazon.com/images/M/MV5BMzU5ZGYzNmQtMTdhYy00OGRiLTg0NmQtYjVjNzliZTg1ZGE4XkEyXkFqcGc@._V1_SX101_CR0,0,101,150_.jpg&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;distributors&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0019701[http] name:_American Movie Classics (AMC)_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0340961[http] name:_5USA_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0164664[http] name:_ARTE_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0014456[http] name:_ARTE_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0014456[http] name:_ARTE_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0014456[http] name:_ARTE_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0195106[http] name:_AXN_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0195106[http] name:_AXN_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0254806[http] name:_AXN_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0337718[http] name:_Amazon Instant Video_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0196006[http] name:_BeTV_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0203432[http] name:_CNBC-e_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0984867[http] name:_Chili Cinema_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0429603[http] name:_Fiji Broadcasting_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0441319[http] name:_Film1 Action_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0365102[http] name:_Film1 Series_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0187155[http] name:_Five US_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0070925[http] name:_Fox Network_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0289973[http] name:_Fuji TV NEXT_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0879800[http] name:_Google Play_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0390640[http] name:_HD suisse_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0488997[http] name:_KVH Media Group_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0851917[http] name:_MagentaTV_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0916189[http] name:_Maxdome_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0131820[http] name:_Monte Carlo Televisión - Canal 4_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0982154[http] name:_NOW!_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0029644[http] name:_Nelonen_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0144901[http] name:_Netflix_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0144901[http] name:_Netflix_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0144901[http] name:_Netflix_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0225813[http] name:_Nova Cinema_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0204647[http] name:_ORF 1_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0308019[http] name:_Orange Cinémax_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0047950[http] name:_Paramount Comedy_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0131276[http] name:_Pop TV_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0304700[http] name:_Prime Series_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0005774[http] name:_RTL Klub Televízió_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0374655[http] name:_RTL Nitro_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0899200[http] name:_Rakuten TV_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0899200[http] name:_Rakuten TV_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0492252[http] name:_SRF zwei_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0015806[http] name:_Schweizer Fernsehen (SF)_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0137851[http] name:_Sony Pictures Home Entertainment_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0137851[http] name:_Sony Pictures Home Entertainment_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0137851[http] name:_Sony Pictures Home Entertainment_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0137851[http] name:_Sony Pictures Home Entertainment_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0137851[http] name:_Sony Pictures Home Entertainment_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0137851[http] name:_Sony Pictures Home Entertainment_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0137851[http] name:_Sony Pictures Home Entertainment_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0137851[http] name:_Sony Pictures Home Entertainment_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0137851[http] name:_Sony Pictures Home Entertainment_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0137851[http] name:_Sony Pictures Home Entertainment_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0137851[http] name:_Sony Pictures Home Entertainment_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0137851[http] name:_Sony Pictures Home Entertainment_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0137851[http] name:_Sony Pictures Home Entertainment_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0137851[http] name:_Sony Pictures Home Entertainment_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0137851[http] name:_Sony Pictures Home Entertainment_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0533814[http] name:_Spike_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0007113[http] name:_SundanceTV_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0203290[http] name:_Super! Drama TV_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0182488[http] name:_TV8_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0440561[http] name:_Universal Channel_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0375381[http] name:_Universal Sony Pictures Home Entertainment_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0362812[http] name:_Videoload_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0018439[http] name:_Vrijzinnig Protestantse Radio Omroep (VPRO)_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0598530[http] name:_e2_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0694131[http] name:_iTunes_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0044989[http] name:_Österreichischer Rundfunk (ORF)_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;genres&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Crime&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Drama&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Thriller&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;imdbID&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0903747&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;kind&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tv series&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;language codes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;en&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;es&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;languages&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;English&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Spanish&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;localized title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Breaking Bad&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;number of seasons&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;original title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Breaking Bad&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;other companies&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0223824[http] name:_Absolut Video_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0056883[http] name:_All Star Motion Picture Catering_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0300363[http] name:_BT Industrial Supply_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0008022[http] name:_Chapman/Leonard Studio Equipment_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0008022[http] name:_Chapman/Leonard Studio Equipment_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0012439[http] name:_Clairmont Camera Film &amp;amp; Digital_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0211783[http] name:_DTC Grip &amp;amp; Electric_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0594011[http] name:_Exchange Communications_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0244588[http] name:_Filmtools_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0184253[http] name:_Hear Kitty_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0076640[http] name:_Keep Me Posted_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0249155[http] name:_LCW Props_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0306663[http] name:_Madison Gate Records_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0306663[http] name:_Madison Gate Records_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0200641[http] name:_PES Payroll_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0009149[http] name:_Pacific Studios II_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0244989[http] name:_Pigfactory_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0709047[http] name:_Prop Movie Money_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0234319[http] name:_ReadySetMedic.com_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0208516[http] name:_Rockbottom Rentals_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0208516[http] name:_Rockbottom Rentals_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0208516[http] name:_Rockbottom Rentals_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0296749[http] name:_Scenechronize_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0029756[http] name:_Serious Grippage &amp;amp; Light_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0248241[http] name:_Sony Pictures Stock Footage_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0075800[http] name:_Soundtrack New York_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0845233[http] name:_Studio Concierge_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0136851[http] name:_The Domain Group_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0449838[http] name:_inVision Subtitling_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;plot&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;A chemistry teacher diagnosed with inoperable lung cancer turns to &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;manufacturing and selling methamphetamine with a former student to &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
          &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;secure his family&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s future.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Walter White is a chemistry genius but works as a chemistry teacher &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;at a high school in Albuquerque, New Mexico. His life drastically &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
          &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;changes when he&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s diagnosed with stage III terminal lung cancer and &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;given a prognosis of two years to live. To ensure that his pregnant &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wife and disabled teenage son have a financial future, he uses his &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
          &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chemistry background to create and sell the world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s finest crystal &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;methamphetamine. To sell his signature &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;blue meth,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; he teams up &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;with Jesse Pinkman, a former student of his. The meth makes them &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;very rich very quickly, but it attracts the attention of his DEA &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
          &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;brother-in-law, Hank. As Walter and Jesse&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s status in the drug &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;world escalates, Walter becomes a dangerous criminal, and Jesse &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;becomes a hot-headed salesman. Hank is always hot on his tail, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;forcing Walter to devise new ways to cover his tracks.—halo1k, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;jackenyon&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;A once-loyal father and chemistry teacher named Walter White turns &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;to a life of crime due to developing Stage 3 terminal lung cancer. &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;He has two years to live. Teaming up with one of his former &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;students, Jesse Pinkman, he grows in power, battles anyone in his &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;way, and raises a family - all while hiding from his &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;brother-in-law, Hank, who works for the &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DEA.—joshchristian-142-732069&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;plot outline&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Walter White is a chemistry genius but works as a chemistry &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
                 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;teacher at a high school in Albuquerque, New Mexico. His &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
                 &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;life drastically changes when he&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s diagnosed with stage III &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;terminal lung cancer and given a prognosis of two years to &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
                 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;live. To ensure that his pregnant wife and disabled teenage &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
                 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;son have a financial future, he uses his chemistry &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
                 &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;background to create and sell the world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s finest crystal &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;methamphetamine. To sell his signature &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;blue meth,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; he teams &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
                 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;up with Jesse Pinkman, a former student of his. The meth &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
                 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;makes them very rich very quickly, but it attracts the &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
                 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;attention of his DEA brother-in-law, Hank. As Walter and &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
                 &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Jesse&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s status in the drug world escalates, Walter becomes a &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dangerous criminal, and Jesse becomes a hot-headed salesman. &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
                 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hank is always hot on his tail, forcing Walter to devise new &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
                 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ways to cover his tracks.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;production companies&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0212797[http] name:_High Bridge Productions_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0071947[http] name:_Gran Via Productions_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0086397[http] name:_Sony Pictures Television_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0019701[http] name:_American Movie Classics (AMC)_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rating&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;9.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;runtimes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;45&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;seasons&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;4&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;5&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;series years&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2008-2013&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sound mix&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Dolby Digital&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;special effects&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Company id:0057238[http] name:_Velocity Visuals_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;synopsis&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Season 1A struggling high school chemistry teacher, Walter &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;White (Bryan Cranston), is diagnosed with inoperable, advanced &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;lung cancer. On a ride along with his DEA agent brother-in-law &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hank Schrader (Dean Norris), Walter sees a former student of &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;his, Jesse Pinkman (Aaron Paul), fleeing the scene of a meth &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;lab. He later contacts Jesse and devises a scheme to become &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;partners in an attempt to combine their skills to manufacture &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;and distribute methamphetamine, with Walter cooking the product &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;and Jesse using his street connections to distribute it. Walter &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;says he wants to provide financial stability for his pregnant &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wife, Skyler (Anna Gunn) and disabled son, and to pay for his &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expensive cancer treatment. During Walter and Jesse&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s first &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;days of selling Albuquerque&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s finest meth, they encounter a &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;series of problems with local drug dealers. Walter takes to &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;selling the meth he produces to a local druglord, Tuco &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Salamanca (Raymond Cruz), using the alias &lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s"&gt;Heisenberg&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s"&gt;.Season &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2Walter continues to find himself facing insurmountable medical &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bills from his cancer treatment. Despite having had several bad &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;experiences while producing meth with Jesse, Walter agrees to &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;rejoin his partner. The two begin producing meth but run into &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;multiple problems. Jesse&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s friend Badger (Matt L. Jones) is &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arrested while selling meth in a sting operation. Walter hires &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;a lawyer, Saul Goodman (Bob Odenkirk), to help Badger. Walter &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;and Jesse drive a recreational vehicle into the desert and &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;produce meth for four days. Later, Combo, another of Jesse&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;friends and distributors, is killed by a rival gang for selling &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;meth in their territory. Saul suggests the two find a new &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;distribution model. Throughout this, Jesse has been building a &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;relationship with his neighbor and landlord, Jane Margolis &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;(Krysten Ritter). Jane, who is a recovering addict, relapses &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;and the two begin doing heroin. Saul finds them a new business &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;partner, Gus Fring (Giancarlo Esposito), who is willing to pay &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$1.2 million for the 38 pounds of meth they produced. Walter &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hastily delivers the product to Gus, but misses his daughter&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;birth. Walt withholds Jesse&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s half of the money because of his &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;drug use, but Jane finds out about it and blackmails Walt. Walt &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;visits Jesse&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s house and witnesses Jane overdosing and choking &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;on her own vomit, but perceiving her as an enemy, sits idly &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;beside her and lets her die. Skyler confronts Walter about his &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;frequent absences and excuses. She begins to piece together his &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;secret life and demands that they separate.Season 3Walter &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wishes to reunite his family, but Skyler is still suspicious of &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Walter&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s second life. Walter believes he can mend the tension &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;between them by confessing to her that he has been producing &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;meth. Skyler is appalled by the confession and demands a formal &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;divorce. Meanwhile, Gus offers to pay Walter $3M for three &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;months of his service. He even offers to provide Walter with a &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;state-of-the-art production facility and a brilliant lab &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;assistant, Gale (David Costabile). Jesse is continuing to &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;produce and sell meth by himself.Hank is working with the DEA &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;to investigate Jesse and is slowly gathering evidence to make &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;an arrest. He survives an assassination attempt made by Tuco&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;twin cousins and manages to kill one of his assailants and &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;critically injure the other, who later dies in the hospital. &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hank suffers critical wounds but survives.Jesse threatens to &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;report Walter to the police if he is arrested, but Walter &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;offers him Gale&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s position at the lab. After obtaining the &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;position, Jesse begins stealing meth from the lab and selling &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;it in secret on the side. Jesse gets romantically involved with &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;a woman he meets in his rehab group and learns her kid brother, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;age 11, was put up by Gus and his street dealers to kill Combo. &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Jesse decides to avenge Combo. Walter aids Jesse in escaping &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;from Gus&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s wrath. Gus begins to lose trust in Walter and asks &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Gale to take over the lab. He orders his henchmen to kill &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Walter and Jesse. After he is abducted by the henchmen, Walter &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;instructs Jesse over the phone to kill Gale in order to force &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Gus not to kill Walter (and, by extension, Jesse) lest he &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;eliminate his only remaining trained chemist.Season 4Jesse &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;follows Walter&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s instructions and murders Gale. Gus decides to &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;discipline the two by enforcing stricter policies at the lab. &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;He also tries to break Walter and Jesse&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s friendship by &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;assigning them to separate work details. While Walter works in &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;the meth lab, Jesse escorts Mike (Jonathan Banks), one of Gus&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;enforcers, to retrieve payments and provide back-up. Walter and &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Jesse become increasingly distant from, and hostile to, each &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;other. Meanwhile, Hank, who has been recovering from his last &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;engagement with the cartel, finds evidence linking Gale to Gus. &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;He believes Gus is a major drug distributor and starts looking &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;for tangible evidence to file charges. Gus realizes Walter&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;close ties with Hank could jeopardize his entire operation. Gus &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;fires Walter and informs him Hank will be killed. He also warns &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Walter that if he intervenes his entire family will be &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;murdered. Jesse and Walter put their differences aside and &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;agree to murder Gus, convincing former cartel enforcer Hector &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Salamanca to detonate a suicide bomb; Hector succeeds in this &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;endeavor, killing himself, Gus, and Tyrus, Gus&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s henchman. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Walter and Jesse then destroy the meth lab and Walter declares &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;to his wife, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;I won.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Season 5Following Gus Fring&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s"&gt;s death, &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Walter partners with Jesse and Mike to create a new meth &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;production and distribution operation. Mike handles all &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;business aspects of the partnership, while Walter and Jesse &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;work with a team of house fumigators to produce meth in tented &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;houses. Hank and the DEA are able to identify nine prison &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;inmates and one lawyer with criminal ties to Mike. Walter kills &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mike, and is fearful that the informants will flip on Walter&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;operation since Mike is no longer able to pay them to keep &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;quiet. He hires Jack Welker, the leader of a neo-Nazi gang, to &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kill the ten informants from within prison. Walter&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s business &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;continues unimpeded until he decides to retire after accruing &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$80 million, which he buries in the desert.Later, Hank is &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;invited to the Whites&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; home, where he unintentionally stumbles &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;upon one of Walter&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s books with a signed message from Gale &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Boetticher. He realizes that Walter is the infamous Heisenberg &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;and secretly restarts the investigation. Hank forms an alliance &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;with Jesse, who now despises Walter for all his wrongdoings. &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Left with no options, Walter hires Jack again to murder Jesse. &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Walter attempts to confront Jesse in the desert, but instead &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hank traps and arrests him. The neo-Nazi gang arrives and &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;engages Hank in a fierce firefight. Jack executes Hank despite &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Walter&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s pleas. Jesse is captured and forced into slavery, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;producing meth for the gang. Before leaving, Jack and his gang &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;take a majority of Walter&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s money, leaving him with only $11 &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;million.Skyler and Walter Jr. are distraught over Hank&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s death &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;and hold Walter accountable. They refuse to leave Albuquerque &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;with Walter and instead contact the police. Walter spends the &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;next several months hiding in a cabin in New Hampshire while &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;struggling with cancer. He returns to New Mexico in order to &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;visit his family one final time and seek revenge against Jack. &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Later that night, Walter executes all of the gang&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s members and &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;frees Jesse, who escapes from the compound before the police &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arrive. Walter realizes he is mortally wounded from a gunshot &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;and slowly succumbs to his injury as the police search the &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
              &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;compound.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Breaking Bad&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;videos&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http://www.imdb.com/title/tt0903747/videoplayer/vi2624111385&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http://www.imdb.com/title/tt0903747/videoplayer/vi3743007513&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;votes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2326262&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;writer&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0319213[http] name:_Vince Gilligan_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0319213[http] name:_Vince Gilligan_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;Person id:0332467[http] name:_Peter Gould_&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;year&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2008&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Run the previous cells to fetch movie details first.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Data Fields You Can Expect
&lt;/h2&gt;

&lt;p&gt;As seen with "Breaking Bad", &lt;code&gt;imdbpy&lt;/code&gt; can fetch a vast amount of data. Here are some common and useful fields you'll often find in the &lt;code&gt;full_movie.data&lt;/code&gt; dictionary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Titles&lt;/strong&gt;: &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;original title&lt;/code&gt;, &lt;code&gt;localized title&lt;/code&gt;, &lt;code&gt;akas&lt;/code&gt; (Also Known As)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Identifiers&lt;/strong&gt;: &lt;code&gt;imdbID&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Type &amp;amp; Year&lt;/strong&gt;: &lt;code&gt;kind&lt;/code&gt; (e.g., 'tv series', 'movie'), &lt;code&gt;year&lt;/code&gt;, &lt;code&gt;series years&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Ratings&lt;/strong&gt;: &lt;code&gt;rating&lt;/code&gt;, &lt;code&gt;votes&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Media&lt;/strong&gt;: &lt;code&gt;cover url&lt;/code&gt;, &lt;code&gt;videos&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Details&lt;/strong&gt;: &lt;code&gt;runtimes&lt;/code&gt;, &lt;code&gt;aspect ratio&lt;/code&gt;, &lt;code&gt;sound mix&lt;/code&gt;, &lt;code&gt;color info&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Content&lt;/strong&gt;: &lt;code&gt;genres&lt;/code&gt;, &lt;code&gt;plot&lt;/code&gt; (often a list), &lt;code&gt;plot outline&lt;/code&gt;, &lt;code&gt;synopsis&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;People &amp;amp; Companies&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;cast&lt;/code&gt;, &lt;code&gt;writer&lt;/code&gt;, &lt;code&gt;director&lt;/code&gt; (lists of &lt;code&gt;Person&lt;/code&gt; objects; access names via &lt;code&gt;person.get('name')&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;production companies&lt;/code&gt;, &lt;code&gt;distributors&lt;/code&gt;, &lt;code&gt;other companies&lt;/code&gt;, &lt;code&gt;special effects&lt;/code&gt; (lists of &lt;code&gt;Company&lt;/code&gt; objects; access names via &lt;code&gt;company.get('name')&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;  &lt;strong&gt;Classification &amp;amp; Localization&lt;/strong&gt;: &lt;code&gt;certificates&lt;/code&gt; (e.g., 'United States:TV-MA'), &lt;code&gt;countries&lt;/code&gt;, &lt;code&gt;country codes&lt;/code&gt;, &lt;code&gt;languages&lt;/code&gt;, &lt;code&gt;language codes&lt;/code&gt;
&lt;/li&gt;

&lt;li&gt;  &lt;strong&gt;For TV Series&lt;/strong&gt;: &lt;code&gt;number of seasons&lt;/code&gt;, &lt;code&gt;seasons&lt;/code&gt; (list of season numbers)&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The exact fields available can vary depending on how much information IMDb has for a particular title. Always use &lt;code&gt;.get()&lt;/code&gt; with a default value or check if a key exists before accessing it directly to avoid errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;imdbpy&lt;/code&gt; is a powerful yet straightforward library for accessing IMDb's rich database. We've covered:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Installation and setup.&lt;/li&gt;
&lt;li&gt; Searching for titles.&lt;/li&gt;
&lt;li&gt; Fetching comprehensive details using a movie ID.&lt;/li&gt;
&lt;li&gt; Exploring the structure of the returned data, with "Breaking Bad" as our guide.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now you have the tools to start building your own movie or TV show-related applications. Imagine the possibilities!&lt;/p&gt;




&lt;h2&gt;
  
  
  💬 Let's Discuss!
&lt;/h2&gt;

&lt;p&gt;What cool projects are you thinking of building with IMDb data? Or what's your favorite obscure movie fact you've discovered? Share your thoughts and ideas in the comments below!&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>imdb</category>
      <category>api</category>
      <category>webdev</category>
      <category>python</category>
    </item>
    <item>
      <title>Real-Time DonationAlerts Notifications with Python, OAuth &amp; WebSockets 🚀</title>
      <dc:creator>Fedor Pasynkov</dc:creator>
      <pubDate>Fri, 02 May 2025 20:53:53 +0000</pubDate>
      <link>https://dev.to/fedor-pasynkov/real-time-donationalerts-notifications-with-python-oauth-websockets-3jep</link>
      <guid>https://dev.to/fedor-pasynkov/real-time-donationalerts-notifications-with-python-oauth-websockets-3jep</guid>
      <description>&lt;p&gt;Hey Devs! 👋&lt;/p&gt;

&lt;p&gt;Ever wanted to react instantly when you receive a donation on DonationAlerts? Maybe trigger an effect in your stream, update a dashboard, or just log donations reliably without constantly hitting an API? Polling APIs works, but it's inefficient and not real-time.&lt;/p&gt;

&lt;p&gt;DonationAlerts offers a way to get these notifications instantly using WebSockets, but setting it up involves navigating the OAuth 2.0 flow and their specific WebSocket protocol (powered by Centrifugo).&lt;/p&gt;

&lt;p&gt;In this guide, we'll walk through how to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Register an application on DonationAlerts.&lt;/li&gt;
&lt;li&gt; Implement the OAuth 2.0 Authorization Code flow using Python (with FastAPI).&lt;/li&gt;
&lt;li&gt; Connect to the DonationAlerts Centrifugo WebSocket.&lt;/li&gt;
&lt;li&gt; Authenticate, subscribe, and receive real-time donation messages.&lt;/li&gt;
&lt;li&gt; Understand the format of the donation data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's ditch the polling and get those donations flowing in real-time!&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  A DonationAlerts account (the streamer account you want to monitor).&lt;/li&gt;
&lt;li&gt;  Python 3.8+ installed.&lt;/li&gt;
&lt;li&gt;  Basic familiarity with Python. FastAPI knowledge is helpful but not strictly required to understand the concepts.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;pip&lt;/code&gt; for installing packages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;(We'll use FastAPI for the web server handling OAuth, but the core logic for interacting with DonationAlerts API and WebSockets can be adapted to other frameworks.)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Register Your App on DonationAlerts 🔑
&lt;/h2&gt;

&lt;p&gt;Before we write any code, we need to tell DonationAlerts about our application and get some credentials.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Go to the &lt;strong&gt;DonationAlerts OAuth Applications page&lt;/strong&gt;: &lt;a href="https://www.donationalerts.com/application/clients" rel="noopener noreferrer"&gt;https://www.donationalerts.com/application/clients&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; Log in using your streamer account (Twitch, YouTube, etc.).&lt;/li&gt;
&lt;li&gt; On the "OAuth API Applications" page, click the &lt;strong&gt;"+ CREATE NEW APP"&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt; Fill in the "New Application" form:

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;App name:&lt;/strong&gt; Give it a descriptive name, like "My Realtime Monitor". (This is shown to users).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Redirect URL:&lt;/strong&gt; This is &lt;strong&gt;CRUCIAL&lt;/strong&gt;. Enter the exact URL where DonationAlerts will send the user back after they authorize your app. For local development with our example, use: &lt;code&gt;http://localhost:8000/api/auth/donationalerts/callback&lt;/code&gt;. If you deploy, this needs to be your public callback URL.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; Click the orange &lt;strong&gt;"CREATE"&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt; Find your newly created app in the list. You'll need two pieces of information:

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;App ID:&lt;/strong&gt; This is your unique application identifier. We'll call it &lt;code&gt;APP_ID&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;API Key:&lt;/strong&gt; This is your application's secret. &lt;strong&gt;Keep this safe and never expose it in frontend code!&lt;/strong&gt; We'll call it &lt;code&gt;API_KEY&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Copy these two values (&lt;code&gt;APP_ID&lt;/code&gt; and &lt;code&gt;API_KEY&lt;/code&gt;). We'll need them soon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Understanding the OAuth 2.0 Flow 🌊
&lt;/h2&gt;

&lt;p&gt;We need the user (the streamer) to grant our application permission to access their data (specifically, view their profile and subscribe to donation events) without giving us their password. That's where OAuth 2.0 comes in. We'll use the "Authorization Code" flow, which is standard for web applications.&lt;/p&gt;

&lt;p&gt;Here's the gist:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;User Initiates:&lt;/strong&gt; The user clicks a "Login with DonationAlerts" button in our app.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Redirect to DonationAlerts:&lt;/strong&gt; Our backend redirects the user's browser to a special DonationAlerts URL, including our &lt;code&gt;APP_ID&lt;/code&gt;, the requested &lt;code&gt;scopes&lt;/code&gt; (permissions), and the &lt;code&gt;redirect_uri&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;User Authorizes:&lt;/strong&gt; The user logs into DonationAlerts (if needed) and sees a screen asking them to approve the permissions our app requested (&lt;code&gt;oauth-user-show&lt;/code&gt;, &lt;code&gt;oauth-donation-subscribe&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Redirect Back with Code:&lt;/strong&gt; If approved, DonationAlerts redirects the user's browser back to our &lt;code&gt;redirect_uri&lt;/code&gt;, adding a temporary &lt;code&gt;code&lt;/code&gt; to the URL.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Exchange Code for Token:&lt;/strong&gt; Our backend receives this &lt;code&gt;code&lt;/code&gt;, verifies it, and then securely makes a &lt;em&gt;server-to-server&lt;/em&gt; request to DonationAlerts, sending the &lt;code&gt;code&lt;/code&gt;, our &lt;code&gt;APP_ID&lt;/code&gt;, and our secret &lt;code&gt;API_KEY&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Receive Tokens:&lt;/strong&gt; DonationAlerts verifies everything and sends back an &lt;code&gt;access_token&lt;/code&gt; (used to make API calls) and a &lt;code&gt;refresh_token&lt;/code&gt; (used to get a new &lt;code&gt;access_token&lt;/code&gt; when the old one expires).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This &lt;code&gt;access_token&lt;/code&gt; is the key to interacting with the DonationAlerts API on the user's behalf.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Implementing OAuth in Python (FastAPI Example) 🐍
&lt;/h2&gt;

&lt;p&gt;Let's set up a simple FastAPI app to handle this flow.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(For a full runnable example including token storage, WebSocket management, and an interactive setup helper, check out the code in the &lt;a href="https://github.com/pefbrute/donationalerts-oauth-websocket-example" rel="noopener noreferrer"&gt;donationalerts-oauth-websocket-example&lt;/a&gt; repository.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configuration (&lt;code&gt;config.py&lt;/code&gt; / &lt;code&gt;.env&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's best practice to store credentials and settings outside your code. We can use a &lt;code&gt;.env&lt;/code&gt; file and a helper &lt;code&gt;config.py&lt;/code&gt; module (as shown in the full example) to load these. Your &lt;code&gt;.env&lt;/code&gt; would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .env
APP_ID="YOUR_APP_ID_HERE"
API_KEY="YOUR_API_KEY_HERE"
REDIRECT_URI="http://localhost:8000/api/auth/donationalerts/callback"
SESSION_SECRET_KEY="GENERATE_A_STRONG_RANDOM_KEY_HERE" # For session security

# Usually fixed
DA_SCOPES="oauth-user-show oauth-donation-subscribe"
DA_AUTHORIZATION_URL="https://www.donationalerts.com/oauth/authorize"
DA_TOKEN_URL="https://www.donationalerts.com/oauth/token"
DA_API_BASE_URL="https://www.donationalerts.com/api/v1"
# ... other URLs if needed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;FastAPI Endpoints (&lt;code&gt;main.py&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We need two main endpoints: one to start the login, and one to handle the callback.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# main.py (Simplified Snippets)
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Depends&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi.responses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RedirectResponse&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;starlette.middleware.sessions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SessionMiddleware&lt;/span&gt; &lt;span class="c1"&gt;# For storing state
&lt;/span&gt;
&lt;span class="c1"&gt;# Assume 'settings' is loaded from config.py or .env
# settings = load_app_config() # From our full example's config.py
&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# IMPORTANT: SessionMiddleware is needed to store the OAuth state temporarily
&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SessionMiddleware&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secret_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SESSION_SECRET_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# --- Login Endpoint ---
&lt;/span&gt;&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/auth/donationalerts/login&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;login_donationalerts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;token_urlsafe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;oauth_state&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="c1"&gt;# Store state to prevent CSRF
&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;client_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;APP_ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redirect_uri&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;REDIRECT_URI&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;response_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;scope&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DA_SCOPES&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;state&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;auth_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DA_AUTHORIZATION_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;authorization_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth_request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Redirecting to: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;authorization_url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# For debugging
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;RedirectResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authorization_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# --- Callback Endpoint ---
&lt;/span&gt;&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/auth/donationalerts/callback&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;auth_donationalerts_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Handle authorization error from DonationAlerts
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# --- Security Check: Verify State ---
&lt;/span&gt;    &lt;span class="n"&gt;stored_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;oauth_state&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;stored_state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# State mismatch, potential CSRF attack!
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Invalid state&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Missing authorization code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# --- Exchange Code for Tokens ---
&lt;/span&gt;    &lt;span class="n"&gt;token_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;exchange_code_for_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# See function below
&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;token_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed to exchange code for token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# --- Success! Store Tokens Securely ---
&lt;/span&gt;    &lt;span class="c1"&gt;# In a real app: save token_data['access_token'], token_data['refresh_token'],
&lt;/span&gt;    &lt;span class="c1"&gt;# and calculate token_data['expires_at'] in a database linked to the user.
&lt;/span&gt;    &lt;span class="c1"&gt;# For this example, we might use a simple file storage (like token_storage.py).
&lt;/span&gt;    &lt;span class="c1"&gt;# save_token(token_data) # From our full example's token_storage.py
&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OAuth successful, tokens obtained!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Redirect user back to the main page of your app
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;RedirectResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/?status=success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;303&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# --- Helper: Token Exchange ---
&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;exchange_code_for_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grant_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;authorization_code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;client_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;APP_ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;client_secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redirect_uri&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;REDIRECT_URI&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DA_TOKEN_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# Check for HTTP errors
&lt;/span&gt;            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Token exchange successful!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPStatusError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Token exchange failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; - &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;An error occurred during token exchange: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This handles the core OAuth flow. Remember to securely store the obtained &lt;code&gt;access_token&lt;/code&gt; and &lt;code&gt;refresh_token&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Connecting to the WebSocket (Centrifugo) 🕸️
&lt;/h2&gt;

&lt;p&gt;Now that we have an &lt;code&gt;access_token&lt;/code&gt;, we can connect to the WebSocket for real-time events. This is where it gets a bit tricky, as it involves &lt;strong&gt;three different types of tokens&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;OAuth &lt;code&gt;access_token&lt;/code&gt;:&lt;/strong&gt; The one we just obtained. Needed for API calls.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;socket_connection_token&lt;/code&gt;:&lt;/strong&gt; A &lt;em&gt;temporary&lt;/em&gt; token specifically for &lt;em&gt;authenticating the WebSocket connection itself&lt;/em&gt;. We get this from the &lt;code&gt;/api/v1/user/oauth&lt;/code&gt; API endpoint using our &lt;code&gt;access_token&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;subscription_token&lt;/code&gt;:&lt;/strong&gt; Another &lt;em&gt;temporary&lt;/em&gt; token needed to &lt;em&gt;subscribe to a specific private channel&lt;/em&gt; (like the donations channel) &lt;em&gt;after&lt;/em&gt; the WebSocket is authenticated. We get this from the &lt;code&gt;/api/v1/centrifuge/subscribe&lt;/code&gt; endpoint using our &lt;code&gt;access_token&lt;/code&gt; and a &lt;code&gt;client_id&lt;/code&gt; provided by the WebSocket upon authentication.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The Connection Sequence:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's the sequence implemented in our example's &lt;code&gt;donationalerts_client.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# donationalerts_client.py (Simplified Snippets)
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;websockets&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;

&lt;span class="c1"&gt;# Assume 'settings' is loaded, and we have a valid 'access_token'
# Also assume '_make_api_request' is a helper function using httpx and access_token
&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;connect_and_listen&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# --- Stage 1: Get User ID and Socket Token (HTTP API) ---
&lt;/span&gt;    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;user_info_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;_make_api_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/user/oauth&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;user_info_response&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;user_info_response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
             &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed to get user info&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_info_response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;socket_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_info_response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;socket_connection_token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;socket_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Missing user_id or socket_token in API response&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error getting user info/socket token: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Got User ID: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, Socket Token: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;socket_token&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Don't log full token
&lt;/span&gt;
    &lt;span class="c1"&gt;# --- Stage 2: Connect and Authenticate WebSocket ---
&lt;/span&gt;    &lt;span class="n"&gt;websocket_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DA_CENTRIFUGO_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;websockets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;websocket_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Connected to WebSocket: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;websocket_url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# --- Stage 2a: Send Socket Token ---
&lt;/span&gt;            &lt;span class="c1"&gt;# IMPORTANT: The format is specific! {"params": {"token": ...}, "id": 1}
&lt;/span&gt;            &lt;span class="n"&gt;auth_payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;params&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;socket_token&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth_payload&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sent WebSocket authentication request.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# --- Stage 2b: Receive Auth Response &amp;amp; Client ID ---
&lt;/span&gt;            &lt;span class="n"&gt;auth_response_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;auth_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth_response_str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Received auth response: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;auth_response&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;client_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;auth_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;auth_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;client&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;client_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auth_response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;client&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WebSocket authenticated! Client ID: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WebSocket authentication failed!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;

            &lt;span class="c1"&gt;# --- Stage 3: Get Subscription Token (HTTP API) ---
&lt;/span&gt;            &lt;span class="n"&gt;channel_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$alerts:donation_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="n"&gt;sub_token_payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;client&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;channels&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;channel_name&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;sub_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;_make_api_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/centrifuge/subscribe&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sub_token_payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;sub_response&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;channels&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sub_response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                     &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed to get subscription token response&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                     &lt;span class="k"&gt;return&lt;/span&gt;

                &lt;span class="n"&gt;subscription_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;channel_info&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sub_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;channels&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]):&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;channel_info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;channel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;channel_name&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;channel_info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="n"&gt;subscription_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;channel_info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                        &lt;span class="k"&gt;break&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;subscription_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                     &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Subscription token for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;channel_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; not found in response.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                     &lt;span class="k"&gt;return&lt;/span&gt;

                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Got Subscription Token: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;subscription_token&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error getting subscription token: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;

            &lt;span class="c1"&gt;# --- Stage 4: Subscribe to Channel (WebSocket) ---
&lt;/span&gt;            &lt;span class="n"&gt;subscribe_payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Use a new ID for this request
&lt;/span&gt;                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;method&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# 1 = subscribe
&lt;/span&gt;                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;params&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;channel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;channel_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;subscription_token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscribe_payload&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sent subscribe request for channel &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;channel_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# --- Stage 5: Listen for Messages ---
&lt;/span&gt;            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Waiting for donation messages...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;message_str&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RAW MESSAGE RECEIVED: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message_str&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="c1"&gt;# Now parse the message (see next step)
&lt;/span&gt;                &lt;span class="c1"&gt;# handle_donation_message(message_str) 
&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;websockets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exceptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConnectionClosedOK&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WebSocket connection closed normally.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WebSocket Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# --- Helper for API Requests (Example) ---
&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_make_api_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
     &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DA_API_BASE_URL&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
     &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
         &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Phew! That's the most complex part. Getting this sequence right is key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Handling Donation Messages 📨
&lt;/h2&gt;

&lt;p&gt;Once subscribed, DonationAlerts will push messages through the WebSocket &lt;em&gt;without&lt;/em&gt; an &lt;code&gt;id&lt;/code&gt; field. The actual donation data is nested inside.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Message Format:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Based on observations, a typical donation message looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"channel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$alerts:donation_YOUR_USER_ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"seq"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Sequence&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Centrifugo&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;---&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;THIS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;actual&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;donation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;---&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;164405432&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Unique&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Donation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ID!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Important&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;deduplication.&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DonatorName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;entered&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;donator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(fallback)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DonatorDA_Username"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;DA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;username&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;logged&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;in&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your message here! &lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt; Can contain newlines."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"message_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;22.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"is_shown"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;If&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;shown&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;standard&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;DA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;widgets&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"amount_in_user_currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1500.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"recipient_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YourStreamerName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"recipient"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Info&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;about&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;recipient&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(you)&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;YOUR_USER_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YourStreamerLogin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YourStreamerName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"avatar"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"URL_to_avatar"&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-04-12 07:15:49"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;time&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;potentially&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;other&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;fields&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Parsing in Python:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_donation_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message_str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message_str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Check if it's a push message (no 'id') and has the expected structure
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;result_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
            &lt;span class="n"&gt;outer_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
            &lt;span class="n"&gt;donation_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;outer_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# The actual donation payload
&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;donation_data&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;result_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;channel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$alerts:donation_&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="c1"&gt;# Extract useful fields (use .get for safety)
&lt;/span&gt;                &lt;span class="n"&gt;donation_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;donation_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;donation_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;donation_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Anonymous&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;donation_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;currency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;donation_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;currency&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;donation_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;donation_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;created_at&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🎉 New Donation! (ID: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;donation_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   From: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   Amount: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   Message: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Strip whitespace/newlines
&lt;/span&gt;                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   Time: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="c1"&gt;# --- TODO: Send this data to your frontend or trigger actions! ---
&lt;/span&gt;                &lt;span class="c1"&gt;# Example: await websocket_manager.broadcast({"type": "donation", "data": donation_data})
&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;result_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;channel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$alerts:donation_&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                 &lt;span class="c1"&gt;# Might be a subscribe confirmation or other message on the channel
&lt;/span&gt;                 &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Received non-donation push on channel: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
             &lt;span class="c1"&gt;# This is likely an ack for one of our requests (id: 1 or 2) or a ping
&lt;/span&gt;             &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Received message with ID: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
             &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Received unhandled message format: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSONDecodeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error decoding JSON: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;message_str&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error handling message: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Inside the WebSocket listening loop:
# async for message_str in ws:
#     handle_donation_message(message_str)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember to handle potential missing fields gracefully using &lt;code&gt;.get()&lt;/code&gt;. The &lt;code&gt;id&lt;/code&gt; field is crucial if you want to prevent processing the same donation multiple times (DonationAlerts might occasionally send duplicates).&lt;/p&gt;

&lt;h2&gt;
  
  
  (Optional) Step 6: Displaying Donations 🖥️
&lt;/h2&gt;

&lt;p&gt;The Python code above just prints the donation. To show it in a web interface, you'd typically use another WebSocket connection &lt;em&gt;between your backend and your frontend&lt;/em&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Backend (&lt;code&gt;websocket_manager.py&lt;/code&gt; / &lt;code&gt;main.py&lt;/code&gt;):&lt;/strong&gt; When &lt;code&gt;handle_donation_message&lt;/code&gt; processes a donation, instead of printing, use a &lt;code&gt;WebSocketManager&lt;/code&gt; to &lt;code&gt;broadcast&lt;/code&gt; the &lt;code&gt;donation_data&lt;/code&gt; to all connected frontend clients.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Frontend (&lt;code&gt;index.html&lt;/code&gt; / JavaScript):&lt;/strong&gt; Establish a WebSocket connection to your backend (&lt;code&gt;/ws&lt;/code&gt;). Listen for messages. When a message of &lt;code&gt;type: "donation"&lt;/code&gt; arrives, use JavaScript to parse the &lt;code&gt;data&lt;/code&gt; and append it to the HTML.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified Frontend JS Example&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`ws://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/ws`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Connect to your backend WS&lt;/span&gt;

&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;donation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Function to add the donation to the page&lt;/span&gt;
            &lt;span class="nf"&gt;displayDonation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error processing message from backend:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;displayDonation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;donation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;donations-log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sender&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;donation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;donation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Anonymous&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;strong&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/strong&amp;gt; donated &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;donation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;donation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;donation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion ✨
&lt;/h2&gt;

&lt;p&gt;We've covered the essential steps to receive real-time DonationAlerts notifications using OAuth 2.0 and WebSockets with Python. It involves setting up an app, handling the multi-step OAuth and WebSocket connection process (including those tricky specific tokens!), and parsing the incoming donation messages.&lt;/p&gt;

&lt;p&gt;While the setup is more involved than simple polling or the older widget token method, it gives you a standard, robust way to integrate directly with the DonationAlerts event stream.&lt;/p&gt;

</description>
      <category>python</category>
      <category>donationalerts</category>
      <category>oauth</category>
      <category>websockets</category>
    </item>
  </channel>
</rss>
