[–] 7 points 5 days ago

I read this recently and also found it kinda underwhelming. I know there's a massive kinda cult following of this series and supposedly this massive layering in the writing that makes it amazing or whatever, but for me, it was just a dull read. It was a bit of a slog for me.

  • source
  • parent
  • context
  • [–] 23 points 2 months ago* (8 children)

    A raspberry pi running Kodi has been serving me well for years.

    Easy install paths include LibreELEC and osmc. I believe both are based on debian.

    FWIW I chose and have been happy with osmc.

    Regardless of the choice, Kodi has a plugin system for things like YouTube, you can access your media via the jellyfin plugin/app, or by pointing Kodi at your media. It has a remote app available on android and iOS, and for the most part just works.

  • source
  • [–] 21 points 2 months ago

    DHH is a Nazi pos, and I hate everyone glazing all over Omarchy.

    I'm a little torn because if it helps raise awareness and desktop adoption if Linux, that's a win, but fuck DHH.

  • source
  •  

    Hello Programming.dev gang,

    I'm still wrapping my head around classes, oop, and scoping, and was hoping to get some feedback. on a class definition.

    Here's the background:

    I'm implementing pydantic for handing my .env secrets and constants (Haven't gotten to a secrets manager yet), with pydantic_settings's BaseSettings and SettingsConfigDict, as well as pydantic's SecretStr.

    I set up a parent class in settings.py to configure the .env file and settings, which you can see below.

    settings.py Parent Class (click to expand)

    from pydantic_settings import BaseSettings, SettingsConfigDict
    from pydantic import SecretStr
    
    
    class Settings(BaseSettings):
        model_config = SettingsConfigDict(
            env_file="/opt/pyprojects/media-scripts/.env.dev", extra="ignore"
        )
    

    I've got a self hosted Baserow database that I'm using for my project, and I defined a class in baserow.py that inherits from the Settings class for the .env config, and has all of the fucntions for interacting with the database as methods of the class, which you can see below.

    (I've got a number of other classes similar to Baserow planned that will inherit from the Settings class, provided I'm on the right track with this layout.)

    I thought it was a good idea to put the methods in the class with the .env var attributes to keep scoping focused and local. I'm wondering if this is a good idea or not, and am also open to any other feedback on the code/classes anyone might have. Thank you to anyone taking the time to read and/or respond.

    baserow.py with Baserow child class (click to expand)

    import json
    
    import requests
    from pydantic import SecretStr
    
    from mods.settings import Settings
    
    
    class Baserow(Settings):
        br_url: str
        br_api: SecretStr
    
        def query_db(self, urlFilters):
            url = f"{self.br_url}?user_field_names=true&{urlFilters}"
            headers = {"Authorization": f"Token {self.br_api}"}
    
            brQueryResults = requests.get(url, headers=headers)
    
            return brQueryResults.json()
    
        def get_all_shows(self):
            url = f"{self.br_url}?user_field_names=true&size=200"
            headers = {"Authorization": f"Token {self.br_api.get_secret_value()}"}
    
            brQueryResults = requests.get(url, headers=headers)
    
            return brQueryResults.json()
    
        def post_db(self, showDictionary):
            brPostResults = requests.post(
                f"{self.br_url}?user_field_names=true",
                headers={
                    "Authorization": f"Token {self.br_api.get_secret_value()}",
                    "Content-Type": "application/json",
                },
                json=showDictionary,
            )
    
            print(json.dumps(brPostResults.json(), indent=2))
    
        def patch_db(self, showDictionary, row):
            brPatchResults = requests.patch(
                f"{self.br_url}?user_field_names=true",
                headers={
                    "Authorization": f"Token {self.br_api.get_secret_value()}",
                    "Content-Type": "application/json",
                },
                json=showDictionary,
            )
    
            print(brPatchResults)
    
        def delete_db_row(self, row):
            brDeleteResults = requests.delete(
                f"{self.br_url}{row}/",
                headers={
                    "Authorization": f"Token {self.br_api.get_secret_value()}",
                },
            )
            print(brDeleteResults)
    

     

    So I'm learning python (by doing, reading, and doing some more), and I've been wanting to automate updating the listening port in my qbittorrent docker container when the gluetun vpn container changes the forwarded port.

    This is what I came up with, which I combined with an hourly cronjob to keep it the listen port updated.

    (Code under the spoiler tag to make this more readable).

    Tap for spoiler

    import re
    import os
    import logging
    from datetime import datetime
    from dotenv import load_dotenv
    
    import docker
    from qbittorrent import Client
    
    # FUNCTION DECLARATION #
    
    
    def log(code, message):
        logFilePath = "/opt/pyprojects/linux-iso-torrents/pf.log"
        logDateTime = datetime.today().strftime("%Y-%m-%d %H:%M:%S")
        message = f"[{logDateTime}] {message}"
        logger = logging.getLogger(__name__)
        logging.basicConfig(
            filename=f"{logFilePath}", encoding="utf-8", level=logging.DEBUG
        )
        match code:
            case "debug":
                logger.debug(message)
            case "info":
                logger.info(message)
            case "warning":
                logger.warning(message)
            case "error":
                logger.error(message)
    
    
    def get_current_forwarded_port():
        client = docker.from_env()
        container = client.containers.get("a40124291102d")
        logs = container.logs().decode("utf-8")
        allPortForwards = re.findall(r".+port forwarded is [0-9]{5}", logs)
        currentForwardedPort = allPortForwards[-1].split(" ")[-1]
    
        return currentForwardedPort
    
    
    def get_current_listening_port():
        qbConfigUrl = "/home/server/docker/qbit/config/qBittorrent/qBittorrent.conf"
        with open(qbConfigUrl) as f:
            config = f.read()
            qbitListenPort = re.search(r"Session\\Port=[0-9]{5}", config)
            currentListeningPort = qbitListenPort.group(0).split("=")[-1]
    
        return currentListeningPort
    
    
    def update_qbittorrent_listen_port(port):
        QB_URL = os.getenv("QB_URL")
        QB_USER = os.getenv("QB_USER")
        QB_PASSWORD = os.getenv("QB_PASSWORD")
        portJSON = {}
        portJSON["listen_port"] = port
        qb = Client(QB_URL)
        qb.login(f"{QB_USER}", f"{QB_PASSWORD}")
    
        qb.set_preferences(**portJSON)
    
    
    # BEGIN SCRIPT #
    
    load_dotenv()
    
    currentForwardedPort = get_current_forwarded_port()
    currentListeningPort = get_current_listening_port()
    
    if currentForwardedPort != currentListeningPort:
        update_qbittorrent_listen_port(currentPort)
        log("info", f"qbittorrent listen port set to {currentForwardedPort}")
    else:
        log("info", "forwarded port and listen port are a match")
    

    There's more I want to do, the next thing being to check the status of both containers and if one or both are down, to log that and gracefully exit, but for now, I'm pretty happy with this (open to any feedback, always willing to learn).

     

    Question for you all.

    I was working on a bash script and working out some logic for command chaining and grouping for executing different functions in my script based on the return status of other functions.

    I know you can group commands with (), which spawns a subshell, and within the grouping you can chain with ;, &&, and ||.

    I also know that you can group commands with {}, but, within the curly braces you can only separate commands with ;.

    EDIT: the above statement of the curly braces only allowing ; is incorrect. I misunderstood what I had read. @SheeEttin@lemmy.zip pointed out my mistake.

    The requirement is that the list of commands in the curly braces needs to be terminated with a semicolon or a newline character, and in the script, I unknowingly was meeting the rules by using the newlines to make the code easier to read for myself.

    END EDIT:

    In the script, for readability I did group the commands across lines kind of like a function.

    The script is pretty simple. I created a few functions with echo commands to test the logic. the script asks for input of foo or bar. The first function has an if, and if the input is foo, it executes. If it's bar it returns 1.

    The return of 1 triggers the or (||) and executes the second command group.

    The idea was, if the user inputs foo, the first group triggers printing foo baz to stdout. If the user inputs bar, the foo function returns 1, the baz function does not execute, the or is triggered, and bar but is printed to stdout

    Here's the script (which executes as described):

    Can anyone explain why I'm able to group with curly braces and still use && inside them?

    (Also, the reason I want to use the curly braces is I don't want to spawn a subshell. I want variable persistence in the current shell)

    #! /usr/bin/bash
    
    # BEGIN FUNCTIONS #
    
    foo () {
    
        if [[ "${input}" = foo ]]; then
            echo "foo"
            return 0
        else
            return 1
        fi
    
    }
    
    bar () {
    
        echo "bar"
    
    }
    
    baz () {
    
        echo "baz"
    
    }
    
    but () {
    
        echo "but"
    
    }
    
    # END FUNCTIONS #
    
    read -p "foo or bar? " input
    
    {
        foo && 
        baz
    } ||
    
        {
            bar &&
            but
        }
    
    view more: next ›