Wednesday, December 10, 2025

Debian 13: rebuilding nvidia driver after kernel upgrade from linux-image-6.12.31-amd64 to linux-image-6.12.57+deb13-amd64

After you upgrade kernel, in my case linux-image-6.12.31-amd64 to linux-image-6.12.57+deb13-amd64, you need to rebuild nvidia driver. During boot, Debian will show some error loading nvidia module

To check which module fail during boot 

# journalctl -b -u systemd-modules-load.service 
Dec 10 09:48:02 mylocalpc systemd-modules-load[781]: modprobe: ERROR: Error running install command 'modprobe n> 
Dec 10 09:48:02 mylocalpc systemd-modules-load[781]: modprobe: ERROR: could not insert 'nvidia_modeset': Invali> 
Dec 10 09:48:02 mylocalpc systemd-modules-load[789]: modprobe: FATAL: Module nvidia-current-drm not found in di> 
Dec 10 09:48:02 mylocalpc systemd-modules-load[767]: Error running install command 'modprobe nvidia-modeset ; m> 
Dec 10 09:48:02 mylocalpc systemd-modules-load[767]: Failed to insert module 'nvidia_drm': Invalid argument 
Dec 10 09:48:02 mylocalpc systemd[1]: systemd-modules-load.service: Main process exited, code=exited, status=1/> 
Dec 10 09:48:02 mylocalpc systemd[1]: systemd-modules-load.service: Failed with result 'exit-code'. 
Dec 10 09:48:02 mylocalpc systemd[1]: Failed to start systemd-modules-load.service - Load 

To install dkms

root@mylocalpc:~# apt install dkms
dkms is already the newest version (3.2.2-1~deb13u1).
dkms set to manually installed.
Summary:
  Upgrading: 0, Installing: 0, Removing: 0, Not Upgrading: 1

To show dkms status

root@mylocalpc:~# dkms status
nvidia-current/550.163.01, 6.12.31-amd64, x86_64: installed 

To rebuild nvidia driver, it will build any kernel installed.

root@mylocalpc:~# dkms install nvidia-current/550.163.01
Sign command: /lib/modules/6.12.57+deb13-amd64/build/scripts/sign-file
Signing key: /var/lib/dkms/mok.key
Public certificate (MOK): /var/lib/dkms/mok.pub

Building module(s)................ done.
Signing module /var/lib/dkms/nvidia-current/550.163.01/build/nvidia.ko
Signing module /var/lib/dkms/nvidia-current/550.163.01/build/nvidia-modeset.ko
Signing module /var/lib/dkms/nvidia-current/550.163.01/build/nvidia-drm.ko
Signing module /var/lib/dkms/nvidia-current/550.163.01/build/nvidia-uvm.ko
Signing module /var/lib/dkms/nvidia-current/550.163.01/build/nvidia-peermem.ko
Installing /lib/modules/6.12.57+deb13-amd64/updates/dkms/nvidia-current.ko.xz
Installing /lib/modules/6.12.57+deb13-amd64/updates/dkms/nvidia-current-modeset.ko.xz
Installing /lib/modules/6.12.57+deb13-amd64/updates/dkms/nvidia-current-drm.ko.xz
Installing /lib/modules/6.12.57+deb13-amd64/updates/dkms/nvidia-current-uvm.ko.xz
Installing /lib/modules/6.12.57+deb13-amd64/updates/dkms/nvidia-current-peermem.ko.xz
Running depmod.... done.

to check status of nvidia modul

root@mylocalpc:~# dkms status
nvidia-current/550.163.01, 6.12.31-amd64, x86_64: installed
nvidia-current/550.163.01, 6.12.57+deb13-amd64, x86_64: installed

restart your debian. 

Friday, December 5, 2025

Pyhton: playwright to get audio url stream

There are many tool to find url audio stream from radio station's page, here are a few list:

  1. playwright
  2. Selenium
  3. Puppeteer

To use Wget to find url audo on statik and simple web  

$ wget --spider -r -l 5 -nv -w 1 --no-clobber -A .mp3,.m3u8 "https://www.example.com/audio/stations" 2>&1 | grep -E '\.(mp3|m3u8)$' | awk '{print $3}' | sort -u > out_audio_urls.txt

Install playwright on debian with virtual environment:

myuser@mypc:~$ mkdir spider_playwright
myuser@mypc:~$ cd spider_playwright/
myuser@mypc:~/spider_playwright$ python3 -m venv venv
myuser@mypc:~/spider_playwright$ source venv/bin/activate
(venv) myuser@mypc:~/spider_playwright$ pip install playwright
Collecting playwright
  Downloading playwright-1.56.0-py3-none-manylinux1_x86_64.whl.metadata (3.5 kB)
Collecting pyee<14,>=13 (from playwright)
  Downloading pyee-13.0.0-py3-none-any.whl.metadata (2.9 kB)
Collecting greenlet<4.0.0,>=3.1.1 (from playwright)
  Using cached greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl.metadata (4.1 kB)
Collecting typing-extensions (from pyee<14,>=13->playwright)
  Using cached typing_extensions-4.15.0-py3-none-any.whl.metadata (3.3 kB)
Downloading playwright-1.56.0-py3-none-manylinux1_x86_64.whl (46.3 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 46.3/46.3 MB 2.5 MB/s eta 0:00:00
Using cached greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (610 kB)
Downloading pyee-13.0.0-py3-none-any.whl (15 kB)
Using cached typing_extensions-4.15.0-py3-none-any.whl (44 kB)
Installing collected packages: typing-extensions, greenlet, pyee, playwright
Successfully installed greenlet-3.2.4 playwright-1.56.0 pyee-13.0.0 typing-extensions-4.15.0
(venv) myuser@mypc:~/spider_playwright$ playwright install
Downloading Chromium 141.0.7390.37 (playwright build v1194) from https://cdn.playwright.dev/dbazure/download/playwright/builds/chromium/1194/chromium-linux.zip
173.9 MiB [====================] 100% 0.0s
Chromium 141.0.7390.37 (playwright build v1194) downloaded to /home/myuser/.cache/ms-playwright/chromium-1194
Downloading Chromium Headless Shell 141.0.7390.37 (playwright build v1194) from https://cdn.playwright.dev/dbazure/download/playwright/builds/chromium/1194/chromium-headless-shell-linux.zip
104.3 MiB [====================] 100% 0.0s
Chromium Headless Shell 141.0.7390.37 (playwright build v1194) downloaded to /home/myuser/.cache/ms-playwright/chromium_headless_shell-1194
Downloading Firefox 142.0.1 (playwright build v1495) from https://cdn.playwright.dev/dbazure/download/playwright/builds/firefox/1495/firefox-debian-13.zip
96.7 MiB [====================] 100% 0.0s
Firefox 142.0.1 (playwright build v1495) downloaded to /home/myuser/.cache/ms-playwright/firefox-1495
Downloading Webkit 26.0 (playwright build v2215) from https://cdn.playwright.dev/dbazure/download/playwright/builds/webkit/2215/webkit-debian-13.zip
88.1 MiB [====================] 100% 0.0s
Webkit 26.0 (playwright build v2215) downloaded to /home/myuser/.cache/ms-playwright/webkit-2215
Downloading FFMPEG playwright build v1011 from https://cdn.playwright.dev/dbazure/download/playwright/builds/ffmpeg/1011/ffmpeg-linux.zip
2.3 MiB [====================] 100% 0.0s
FFMPEG playwright build v1011 downloaded to /home/myuser/.cache/ms-playwright/ffmpeg-1011

create file myspider.py and customize to your requirements, make it executable

(venv) myuser@mypc:~/spider_playwright$ chmod u+x myspider.py 

source code myspider.py modify it to meet your requirements 

import asyncio
from playwright.async_api import async_playwright

# generated and adjust by chatgpt.com

AUDIO_HINTS = [
    ".mp3", ".aac", ".m3u8", ".ogg", ".opus", ".wav",
    "stream", "/proxy/", "/live", "radio"
]

async def scan_audio_stream(url: str, listen_seconds: int = 15):
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=False)
        page = await browser.new_page()

        # ----- Detect manual browser close -----
        browser_disconnected = asyncio.Event()

        def on_browser_disconnect():
            print("\n[BROWSER CLOSED] Exiting application.")
            browser_disconnected.set()

        browser.on("disconnected", on_browser_disconnect)
        # ---------------------------------------

        found = set()

        async def on_response(res):
            u = res.url.lower()
            ct = res.headers.get("content-type", "").lower()

            if "audio" in ct or any(k in u for k in AUDIO_HINTS):
                if u not in found:
                    print(f"[NEW AUDIO STREAM] {u}")
                    found.add(u)

        page.on("response", on_response)

        print("Opening page...")
        await page.goto(url, wait_until="domcontentloaded")
        await asyncio.sleep(2)

        # ---- Find audio player iframe ----
        candidate_frames = [
            f for f in page.frames
            if any(kw in f.url.lower() for kw in ["player", "radio", "embed"])
        ]
        if not candidate_frames:
            candidate_frames = page.frames  # fallback

        print("Attempting to click play...")
        for f in candidate_frames:
            try:
                for sel in ["button[aria-label='Play']", "button.play", ".jp-play", "button"]:
                    btn = await f.query_selector(sel)
                    if btn:
                        print(f"Clicked play in iframe → {f.url}")
                        await btn.click()
                        break
            except:
                pass

        print(f"\nListening for audio streams up to {listen_seconds} seconds...\n")

        # ----- Wait for 15 seconds OR browser close -----
        try:
            await asyncio.wait_for(browser_disconnected.wait(), timeout=listen_seconds)
        except asyncio.TimeoutError:
            print("\n[TIMEOUT] Done scanning.")
        # -------------------------------------------------

        await browser.close()
        return list(found)


if __name__ == "__main__":
    results = asyncio.run(scan_audio_stream(
        "https://www.example.com", # CHANGE HERE
        listen_seconds=15     # ← Fast scan
    ))

    print("\n===== ALL STREAMS FOUND =====")
    for s in results:
        print(s)

Code

from playwright.sync_api import sync_playwright

def main():
    with sync_playwright() as p:
        browser = p.chromium.launch(
            headless=False   # <-- You can manually interact
        )
        context = browser.new_context()
        page = context.new_page()

        print("🚀 Browser launched. Type the radio station URL manually.")
        print("👉 When you press Play, audio stream URLs will appear here.\n")

        # Capture outgoing requests
        def on_request(request):
            url = request.url

            if (
                ".mp3" in url or
                ".aac" in url or
                ".m3u8" in url or
                ".ogg" in url or
                "stream" in url.lower() or
                request.resource_type == "media"
            ):
                print("🎧 AUDIO STREAM REQUEST FOUND:")
                print(url, "\n")

        page.on("request", on_request)

        # Capture responses containing audio
        def on_response(response):
            url = response.url
            headers = response.headers
            content_type = headers.get("content-type", "")

            if any(fmt in content_type for fmt in ["audio", "mpeg", "aac", "ogg"]):
                print("🎧 AUDIO STREAM RESPONSE FOUND:")
                print(url, "\n")

        page.on("response", on_response)

        # Open a blank page — you will type the URL manually
        page.goto("about:blank")

        # Keep browser open
        browser.wait_for_event("disconnected")


if __name__ == "__main__":
    main()

Enjoy

Thursday, December 4, 2025

Android java: improving application screen form factor

In AndroidManifest.xml we can define screen form factor to used (androidmanifest.xml android:screenOrientation).

These are the options

  1. "unspecified" : The default value. The system chooses the orientation. The policy it uses, and therefore the choices made in specific contexts, might differ from device to device.
  2. "behind" : The same orientation as the activity that's immediately beneath it in the activity stack.
  3. "landscape" : Landscape orientation (the display is wider than it is tall).
  4. "portrait" : Portrait orientation (the display is taller than it is wide).
  5. "reverseLandscape" : Landscape orientation in the opposite direction from normal landscape. Added in API level 9.
  6. "reversePortrait" : Portrait orientation in the opposite direction from normal portrait. Added in API level 9.
  7. "sensorLandscape" : Landscape orientation, but can be either normal or reverse landscape based on the device sensor. The sensor is used even if the user has locked sensor-based rotation. Added in API level 9.
  8. "sensorPortrait" : Portrait orientation, but can be either normal or reverse portrait based on the device sensor. The sensor is used even if the user has locked sensor-based rotation. However, depending on the device configuration, upside-down rotation might not be allowed. Added in API level 9.
  9. "userLandscape" : Landscape orientation, but can be either normal or reverse landscape based on the device sensor and the user's preference. Added in API level 18.
  10. "userPortrait" : Portrait orientation, but can be either normal or reverse portrait based on the device sensor and the user's preference. However, depending on the device configuration, upside-down rotation might not be allowed. Added in API level 18.
  11. "sensor" : The device orientation sensor determines the orientation. The orientation of the display depends on how the user is holding the device. It changes when the user rotates the device. Some devices, though, don't rotate to all four possible orientations, by default. To use all four orientations, use "fullSensor". The sensor is used even if the user locked sensor-based rotation.
  12. "fullSensor" : The device orientation sensor determines the orientation for any of the four orientations. This is similar to "sensor", except this allows for any of the four possible screen orientations regardless of what the device normally supports. For example, some devices don't normally use reverse portrait or reverse landscape, but this enables those orientations. Added in API level 9.
  13. "nosensor" : The orientation is determined without reference to a physical orientation sensor. The sensor is ignored, so the display doesn't rotate based on how the user moves the device.
  14. "user" : The user's current preferred orientation.
  15. "fullUser" : If the user has locked sensor-based rotation, this behaves the same as user, otherwise it behaves the same as fullSensor and allows any of the four possible screen orientations. Added in API level 18.
  16. "locked" : Locks the orientation to its current rotation, whatever that is. Added in API level 18. 

Warning: To improve the layout of apps on form factors with smallest width >= 600dp, the system ignores the following values of this attribute for apps that target Android 16 (API level 36):

  1.     portrait
  2.     landscape
  3.     reversePortrait
  4.     reverseLandscape
  5.     sensorPortrait
  6.     sensorLandscape
  7.     userPortrait
  8.     userLandscape 

To create variant for table,  Goto main activity, on your main activity dropdown select "create table qualifier", it will create copy of main activity that require to modify to match tablet out. the layout is on layout -> main_activity -> main_activity.xml (sw600dp).

I use:

  1. main_activity.xml
  2. main_activity.xml (sw600dp)
main_activity.xml will be use for all mobile device, don't create main_activity (land). main_activity.xml (sw600dp) will be use on tablet. 

In case you want to lock phone to use portrait, delete main_activity.xml (land), you need to remove android:screenOrientation from AndroidManifest.xml. To make it consistent, this is sample of code in java for your main activity

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // 1. Enable edge-to-edge for Java Activities
        // You need to import androidx.activity.EdgeToEdge;
        EdgeToEdge.enable(this);
        super.onCreate(savedInstanceState);
        // Fragment
        // layout portrait or lanscape?
        // portrait my_fragment_container
        // langsacpe fragment_config_container, fragment_main_container
        setContentView(R.layout.activity_m); // general, let system choose between portrait and landscape
        // Check if landscape (two-pane) layout is active
        View config = findViewById(R.id.fragment_config_container);
        View main = findViewById(R.id.fragment_main_container);
        isTwoPane = (config != null && main != null); // already checked not null
        // Conditional Orientation Lock
        if (!isTwoPane) {
            // If single-pane (phone), force portrait before fragment transactions
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        }
        if (isTwoPane) {
            // dual-pane
            setFragment(R.id.fragment_config_container, new FragmentConfig());
            setFragment(R.id.fragment_main_container, new FragmentMain());
        } else {
            // single-pane
            // all phone must use this activity_m.xml (land)
            setFragment(R.id.my_fragment_container, new FragmentMain());
        } 

Immediately, lock screen for single pane (phone) to portrait, i.e. setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); 

Wednesday, December 3, 2025

Debian 13: bash script to show nvme/ssd healthy and cpu temperature

Requirement

  1. smartmontools
  2. lm-sensors
  3. psensor 

Installation

# apt-get install smartmontools lm-sensors psensor

copy paste this script to show general temperature on nvme/ssd and cpu. It also give information about nvme/ssd healthy.

#!/bin/bash
# this code generated by chatgpt.com no sign in
# each step was interactive, output from pc will be feed to chatgpt to generate correct output

DRIVE=${1:-/dev/sda}

echo "=== SMART Drive Information ($DRIVE) ==="

sudo smartctl -H -A "$DRIVE" | awk '
/^SMART overall-health/      {print "Drive Health: " $NF}
/^194 Temperature_Celsius/   {print "Drive Temperature: " $10 "°C"}
/^190 Airflow_Temperature/   {print "Drive Temperature: " $10 "°C"}
'

echo
echo "=== CPU Temperature ==="
sensors | awk '
/k10temp/ {found=1}
/temp1:/ && found {print "CPU Temp:", $2; found=0}
'

echo
echo "=== GPU Temperature (AMDGPU) ==="
sensors | awk '
/amdgpu/ {found=1}
/edge:/ && found {print "GPU Temp:", $2; found=0}
'

echo
echo "=== ACPI Temperatures ==="
sensors | awk '
/acpitz/ {block=1}
/temp/ && block {print "ACPI " $1, $2}
/^\s*$/ {block=0}
'

Change permission

# chmod u+x ./checkme.sh

Run as sudo or root 

Tuesday, December 2, 2025

Android java: Releasing resource Activity/Fragment <-> AndroidViewModel <-> MyController

Activity/Fragment <-> AndroidViewModel <-> MyController

MyController will hold

  1. Executor
  2. MediaController 
  3. Network access
  4. Database access
  5. Application context for above operation  

Proper way to clear the resource

  1. Executor
            // MyAndroidViewModel onClear() has been reached
            if (myExecutor!=null) {
                myExecutor.shutdown();
                myExecutor = null;
            }
  2. Media Controller
            if (myMediaController!=null) {
                myMediaController.releaseMediaSession(); // MUST BE CLEAR
                myMediaController = null;
            }
  3. Network access
            myGetRadioLogo=null;
  4. Database access  
            myDBAccess=null;
  5. Application context
            appContext=null; // MUST SET NULL AT ONCLEAR

Fail to release these resources may lead to memory leak

Fail to release resource with not properly sequence may lead application crashed, this crash can be found on logcat.