Skip to main content

Posts about hacking

Fixing a Patreon feed's cover artwork

Antennapod With the recent Pocketcast PR blunder I finally decided to jump back to open source Antennapod. This has been painless especially with the introduction of the Remove silence feature, the main feature that kept me with Pocketcasts for so long.

The only issue I had was a Private feed with broken cover artwork, this was frustrating but it looks to be an issue on Patreons end. Thankfully Node Red is available to rescue the situation!

The flow is really simple, the only additional node I have added is the node-red-contrib-httpauth.

  1. On a HTTP request, fetch the RSS feed.
  2. Convert from XML to and Object.
  3. Replace[0].image[0].url[0] with a good url from the podcasters website.
  4. Create the txt/xml headers
  5. Return the fixed RSS Feed

Node-Red Flow

[{"id":"8d2a4ad1.4599a","type":"http request","z":"d437ad18.0999c","name":"","method":"GET","ret":"txt","paytoqs":false,"url":"","tls":"","proxy":"","authType":"","x":367.5,"y":31,"wires":[["4c5cdedd.6772c8"]]},{"id":"4c5cdedd.6772c8","type":"xml","z":"d437ad18.0999c","name":"XML To Object","property":"payload","attr":"","chr":"","x":230.5,"y":78,"wires":[["adbd8c2e.7fbcd"]]},{"id":"7475a34e.9f953c","type":"http in","z":"d437ad18.0999c","name":"rss","url":"/mystupidRSS","method":"get","upload":false,"swaggerDoc":"","x":69.5,"y":31,"wires":[["26c3aede.f41d4a"]]},{"id":"26c3aede.f41d4a","type":"node-red-contrib-httpauth","z":"d437ad18.0999c","name":"","file":"","cred":"","authType":"Basic","realm":"","username":"","password":"","hashed":false,"x":213.5,"y":31,"wires":[["8d2a4ad1.4599a"]]},{"id":"56731f49.b7f77","type":"xml","z":"d437ad18.0999c","name":"Object to XML","property":"payload","attr":"","chr":"","x":647.5,"y":79,"wires":[["5aa22446.612044"]]},{"id":"5aa22446.612044","type":"change","z":"d437ad18.0999c","name":"Set Headers","rules":[{"t":"set","p":"headers","pt":"msg","to":"{}","tot":"json"},{"t":"set","p":"headers.content-type","pt":"msg","to":"text/xml","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":219,"y":131,"wires":[["8df07127.e9d0c8"]]},{"id":"8df07127.e9d0c8","type":"http response","z":"d437ad18.0999c","name":"","statusCode":"","headers":{},"x":391,"y":131,"wires":[]},{"id":"adbd8c2e.7fbcd","type":"change","z":"d437ad18.0999c","name":"Replace Cover Image","rules":[{"t":"set","p":"[0].image[0].url[0]","pt":"msg","to":"","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":440.5,"y":79,"wires":[["56731f49.b7f77"]]}]

Something neat I did with FitNotes and Tasker

FitNotes App for Android I use the fitness tracking app FitNotes on Android. It's a great application that I have happily used for years. The greatest issue I had with it was manually entering my body weight. Well I finally got myself into gear and fixed that issue using the fantastic staple of Android automation, Tasker

Using a Xiaomi Mi Smart Scale that I hooked up to my home server using my python gatttool wrapper I Dump it's weight data into a Google spreadsheet.

Using my Simple API (Because Google's own API is a pain) and the helper task getformatteddate, I pull a unix timestamp and the weight onto my Phone. I then run a INSERT SQL command on FitNote's database (using root of course)

I also did a bulk body weight record import via .csv using sqlitebrowser. Now I no longer have to manually enter my body weight, should have done this years ago.

Here is my Tasker task if you are interested:

    Healthapi (44)
        A1: Flash [ Text:%date %time Long:Off ]
        A2: Flash [ Text:Updating Health Report Long:Off ]
        A3: HTTP Get [ Server:Port: Path:/macros/s/myprivatesheet/exec Attributes:key=myapikey Cookies: User Agent: Timeout:20 Mime Type: Output File: Trust Any Certificate:Off ]
        A4: Variable Set [ Name:%data To:%HTTPD Recurse Variables:Off Do Maths:Off Append:Off ]
        A5: Variable Set [ Name:%newline To:
     Recurse Variables:Off Do Maths:Off Append:Off ]
        A6: Variable Split [ Name:%data Splitter:%newline Delete Base:Off ]
        A7: Variable Split [ Name:%data1 Splitter:, Delete Base:Off ]
        A8: Read File [ File:Tasker/lastdate.dat To Var:%lastdate Continue Task After Error:On ]
        A9: If [ %lastdate neq %data11 ]
        A10: Write File [ File:Tasker/lastdate.dat Text:%data11 Append:Off Add Newline:Off ]
        A11: Perform Task [ Name:getFormattedDate Priority:%priority Parameter 1 (%par1):%data11 Parameter 2 (%par2):yyyy-mm-dd Return Value Variable:%date Stop:Off ]
        A12: Perform Task [ Name:getFormattedDate Priority:%priority Parameter 1 (%par1):%data11 Parameter 2 (%par2):hh:nn:ss Return Value Variable:%time Stop:Off ]
        A13: Variable Set [ Name:%measurement_id To:1 Do Maths:Off Append:On ]
        A14: Variable Set [ Name:%value To:%data12 Do Maths:Off Append:On ]
        A15: Variable Set [ Name:%query To:INSERT INTO MeasurementRecord (measurement_id, date, time, value, comment) VALUES ("%measurement_id", "%date", "%time", "%value",""); Do Maths:Off Append:Off ]
        A16: SQL Query [ Mode:Raw File:/data/data/com.github.jamesgay.fitnotes/databases/database.db Table: Columns: Query:%query Selection Parameters: Order By: Output Column Divider: Variable Array:%test Use Root:On ]

I hope you found this interesting, if only in the abstract "hey that's a thing you can totally do" kind of way. If you want me to write a complete how-to let me know in the comments down below.