Python – Simple URL Extractor

This one is pretty basic tier, but more useful than most. I had a website full of links to pages of video files. I wanted a list that I could stick into yt-dlp. I could do a bunch of copy and pasting, or I could use Python to scrape and take all the links.

It takes a URL as a CLI Argument, specifically like:

> main.py https://website.com

It skims the page with Beautiful Soup and spits out a text file with the date-time-url.txt format. Useful if a site changes over time.

The site I was scraping was using some relative links, so it checks for “http” in the URLs and if its present, just dumps the URL, otherwise, it prepends “REPLACEME” in front of the link, so it’s easy to do a Find/Replace operation and add whatever the full URL is.

For example, if the URL is “/video/12345.php”, which takes you to “website.com/video/12345.php”, it outputs “REPLACEME /video/12345.php” on each line. It’s easy to then replace the “REPLACEME” with the URL on 1-1000+ URLs. I didn’t just add the URL because, at least for my use case, the links added a bit more than just the base URL, and I wanted it to function more universally.

Anyway, here is the script. I don’t think it uses any non-standard library that would need a pip install or anything but if it does, it’ll complain and tell you what to do.

## Simple URL Extractor
## ToUse, at CLI $> python3 main.py [Replace With URL No Braces]
## Will output a list of all href links on a page to a file witht he date time and URL.
## Useful for pushing to a bulk downloader program, though it does not processing so URLs may need to be edited
## If there is not full URL, it pre prends an easily find/replaceable slug

import httplib2
import sys
from datetime import datetime
from bs4 import BeautifulSoup, SoupStrainer

current_datetime = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")

try:
    url = sys.argv[1]
except IndexError:
    print("Error: No URL Defined! Please use main.py [URL]")
    sys.exit(1)

http = httplib2.Http()
status, response = http.request(url)
filename = f"{current_datetime}-{url.split('//')[1].replace('/','_')}.txt"

with open(filename, "x") as f:
    for link in BeautifulSoup(response, 'html.parser', parse_only=SoupStrainer('a')):
        if link.has_attr('href'):
            #print(link['href'])
            write_link = link['href']
            if "http://" not in write_link:
                write_link = f"REPLACEME_{write_link}"
            f.write(f"{write_link}\n")


## Reference
## https://stackoverflow.com/questions/1080411/retrieve-links-from-web-page-using-python-and-beautifulsoup
## https://stackoverflow.com/questions/4033723/how-do-i-access-command-line-arguments
## https://stackoverflow.com/questions/14016742/detect-and-print-if-no-command-line-argument-is-provided

Coding with Perplexity AI – Hirst Painting Drawer

Ive not been using AI a lot, frankly, I find it to be pretty lame for the most part, the images are almost always weirdly uncanny and ugly, and the writing is just bland. I’ve heard it’s pretty good at coding though, and I have not tried using it for code at all. So I decided to give it a go. Specifically, I wanted to use it to augment an existing project from when I was taking that 100 Days of Python course. Specifically, Day 18, the Hirst Painting Project.

The full original code is here:

import colorgram
from turtle import Turtle, Screen
from random import choice
turtle.colormode(255)

#Sample image, cover of CHVRCHES Every Open Eye Album
color_extracted = colorgram.extract("image.jpg", 20)
color_choices = []
for i in color_extracted:
    color_choices.append((i.rgb.r, i.rgb.g, i.rgb.b))

# debug print(color_choices)
#color_choices = [(193, 137, 150), (128, 74, 88), (22, 28, 47), (59, 32, 48), (219, 210, 206), (184, 161, 155), (17, 11, 11), (174, 101, 116), (217, 179, 189), (148, 152, 159), (94, 47, 60), (93, 104, 114), (227, 201, 206), (154, 159, 156), (122, 83, 78), (209, 183, 180), (203, 206, 202), (164, 109, 105), (81, 95, 91), (59, 60, 74)]


marker = Turtle()
marker.speed(100)
marker.penup()
marker.hideturtle()

for y_coord in range(1,10):
    for x_coord in range(0,10):
        marker.setpos(-300 + x_coord * 50,-280 + y_coord * 50)
        marker.pencolor(choice(color_choices))
        marker.dot(30)

screen = Screen()
screen.screensize(600, 600)
screen.exitonclick()

This code, which is also on GitHub with the sample, image, but any image will work, will read the color pallet of a file, “image.jpg”, then draws a simple series of dots in the style of a painting by Damien Hirst. I didn’t pick the theme, it was part of the course, but I do think the result is simple and quite neat.

I have for a while, wanted to make a few updates to this simple program, and sort of tried to a few times, but this time, I let AI do the work. I really wanted two main features.

  • The ability to open any file, instead of having to put a file in the folder and rename it image.jpg
  • The ability to export the result to an image file

I chose Perplexity AI for my assistant. I wanted to use it as a sort of, accompanying tool, rather than letting ti write all the code. I already have the simple drawing code.

I started by asking it for a simple request:

Can you create a python script that will open a turtle graphics window, 1024x768 in size, draw a circle of 5 pixels thickness, diameter 100 pixels, and include a button that will export the canvas to a png or jpg file

Which it did, I could run the code, it would draw a circle, then I could click a button and save an image of a circle. Though, I did come across an issue I never quite fixed.

It would save the dialogue box along with the canvas. It’s basically just, taking a screen shot.

The solution at the moment is to make sure I drag the dialog box off to the side before saving.

Next:

Can you add a "file Open" button at the bottom that passes the file in as a variable and does not draw anything until a file as been selected

Initially, I wanted to make sure it wouldn’t draw an image until the file was loaded, so the file select box doesn’t actually do anything. Later I changed it to allow for drawing without selecting an image, it just defaults to the ‘Every Open Eye’ color set in my original code.

This worked out as expected as well. Now I had the basic structure to slip my existing code in. I had a file as a variable and a mechanism that drew something (currently, a circle). The code it was giving me though, used a class structure though, which is fine, but my existing code doesn’t. I managed to insert my dots drawing code fine, this required renaming some variables to align, specifically, all of the ‘marker’ variables became “self.pen” at the appropriate location. I had trouble though getting the colormode to work properly. I wasn’t sure where to put it in the code, as relating to the class structure.

I have to say, I probably had it correct, but I also realized later I was having some virtual environment issues between VS Code and my venv and the system. Despite VSCode showing that the imports were resolved, when running things, I got not found’ errors. I ended up just running the code from a venv sourced terminal outside of VSCode. It”s a problem to be fixed later.

The first problem that came up here though, Perplexity had added a function that would display the loaded image, as a backdrop behind the dots. This is not the functionality I wanted.

I just found the function and stripped it out manually.

Then I found it again, because it loaded the image as a backdrop when opening a file, and then again when drawing the dots.

Something also notable here. At one point, I took my working code, with the draw dots, and fed it back to Perplexity, telling it, ‘I added some code, please make this the new baseline.’ This worked out, perfectly. Going forward, it worked off my updated code. Even more surprisingly, it detected the new function of the drawing, to draw dots, instead of a single circle, and it renamed the internal references ON ITS OWN.

I was pretty impressed with that.

I had the basic functionality down, but wanted to do some cleanup. After running it over and over, and having to navigate to a directory with pictures each time, I asked it to change the file open and save to default to the user’s home folder. I also asked it to only look for image files, to avoid errors from other file types. I also had it resize the image down to center and fit the dots better.

I couldn’t solve the dialogue box option. I tried to, It added a short delay on the save, but that just reulted in a saved image of the things behind the drawing. I tried to get it to position the save dialog outside of the window, but the code there didn’t seem to actually DO anything.

I also added a few last-minute features. One, is a way to update the background color. It had actually had this feature originally, but I asked it to remove it, because at the time, I didn’t want it.

I also had it add some boxes that allow for selecting how many rows and columns will be drawn. I may look into having it draw larger canvases or maybe things that are not dots in the future. It’s pretty functional as it is though. Well, at least as functional as a program that makes dot images can be.

Anyway, I’ll make a GitHub Repository probably, but for now, the full updated code is below. Also, it turns out I can share my Perplexity chat, so you can also see the full chat here.

## pip install colorgram.py

import turtle
import colorgram
from tkinter import *
from tkinter import filedialog
from tkinter import colorchooser
from random import choice
from PIL import ImageGrab
import os

class TurtleApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Turtle Graphics with Image Open and Save")
        
        self.canvas_width = 600
        self.canvas_height = 550
        
        self.canvas = Canvas(root, width=self.canvas_width, height=self.canvas_height)
        self.canvas.pack()
        
        self.screen = turtle.TurtleScreen(self.canvas)
        self.screen.colormode(255)
        self.screen.bgcolor("white")
        
        self.pen = turtle.RawTurtle(self.screen)
        self.pen.pensize(5)
        self.pen.hideturtle()
        
        self.file_path = None
        self.color_choices = [(193, 137, 150), (128, 74, 88), (22, 28, 47), (59, 32, 48), (219, 210, 206), 
                              (184, 161, 155), (17, 11, 11), (174, 101, 116), (217, 179, 189), (148, 152, 159), 
                              (94, 47, 60), (93, 104, 114), (227, 201, 206), (154, 159, 156), (122, 83, 78), 
                              (209, 183, 180), (203, 206, 202), (164, 109, 105), (81, 95, 91), (59, 60, 74)]
        
        self.rows = 9
        self.columns = 10
        
        self.create_widgets()

    def create_widgets(self):
        button_frame = Frame(self.root)
        button_frame.pack(side=BOTTOM, fill=X)

        open_button = Button(button_frame, text="Open Image", command=self.open_file)
        open_button.pack(side=LEFT, padx=5, pady=5)
        
        draw_button = Button(button_frame, text="Draw Dots", command=self.draw_dots)
        draw_button.pack(side=LEFT, padx=5, pady=5)
        
        save_button = Button(button_frame, text="Export Canvas", command=self.save_canvas)
        save_button.pack(side=LEFT, padx=5, pady=5)

        bg_color_button = Button(button_frame, text="Change Background", command=self.change_background_color)
        bg_color_button.pack(side=LEFT, padx=5, pady=5)

        exit_button = Button(button_frame, text="Exit", command=self.exit_app)
        exit_button.pack(side=LEFT, padx=5, pady=5)

        # Add row and column input
        row_label = Label(button_frame, text="Rows:")
        row_label.pack(side=LEFT, padx=5, pady=5)
        self.row_entry = Entry(button_frame, width=5)
        self.row_entry.insert(0, str(self.rows))
        self.row_entry.pack(side=LEFT, padx=5, pady=5)

        col_label = Label(button_frame, text="Columns:")
        col_label.pack(side=LEFT, padx=5, pady=5)
        self.col_entry = Entry(button_frame, width=5)
        self.col_entry.insert(0, str(self.columns))
        self.col_entry.pack(side=LEFT, padx=5, pady=5)

    def open_file(self):
        # Get the user's home directory
        home_dir = os.path.expanduser("~")
        
        self.file_path = filedialog.askopenfilename(
            initialdir=home_dir,  # Set initial directory to user's home folder
            filetypes=[
                ("Image files", "*.png *.jpg *.jpeg *.gif *.bmp"),
                ("PNG files", "*.png"),
                ("JPEG files", "*.jpg *.jpeg"),
                ("GIF files", "*.gif"),
                ("BMP files", "*.bmp")
            ]
        )
        if self.file_path:
            print(f"Image selected: {self.file_path}")
            self.root.title(f"Turtle Graphics - {os.path.basename(self.file_path)}")
            self.extract_colors()

    def change_background_color(self):
        color = colorchooser.askcolor(title="Choose background color")
        if color[1]:  # color is in the format ((r, g, b), hexcode)
            self.screen.bgcolor(color[1])
            print(f"Background color changed to {color[1]}")

    def extract_colors(self):
        color_extracted = colorgram.extract(self.file_path, 20)
        self.color_choices = []
        for i in color_extracted:
            self.color_choices.append((i.rgb.r, i.rgb.g, i.rgb.b))
        print("Colors extracted from the image")

    def draw_dots(self):
        self.pen.clear()
        
        try:
            self.rows = int(self.row_entry.get())
            self.columns = int(self.col_entry.get())
        except ValueError:
            print("Invalid row or column value. Using default values.")

        self.pen.speed(100)
        self.pen.penup()
        self.pen.hideturtle()

        dot_size = 30
        spacing_x = self.canvas_width / (self.columns + 1)
        spacing_y = self.canvas_height / (self.rows + 1)
        start_x = -self.canvas_width / 2 + spacing_x
        start_y = self.canvas_height / 2 - spacing_y

        for y_coord in range(self.rows):
            for x_coord in range(self.columns):
                self.pen.setpos(start_x + x_coord * spacing_x, start_y - y_coord * spacing_y)
                self.pen.pencolor(choice(self.color_choices))
                self.pen.dot(dot_size)

    def save_canvas(self):
        # Get the main window's position and size
        window_x = self.root.winfo_x()
        window_y = self.root.winfo_y()
        window_width = self.root.winfo_width()
        
        # Calculate the position for the dialog box
        dialog_x = window_x + window_width + 10  # 10 pixels to the right of the main window
        dialog_y = window_y
        
        # Get the user's home directory
        home_dir = os.path.expanduser("~")
        
        # Open the save dialog at the calculated position
        self.root.update()  # Ensure the window size is updated
        save_path = filedialog.asksaveasfilename(
            parent=self.root,
            defaultextension=".png",
            filetypes=[("PNG files", "*.png"), ("JPEG files", "*.jpg *.jpeg")],
            initialdir=home_dir,  # Set initial directory to user's home folder
        )
        
        if save_path:
            # Move the dialog to the desired position
            self.root.geometry(f"+{dialog_x}+{dialog_y}")
            
            x0 = self.root.winfo_rootx() + self.canvas.winfo_x()
            y0 = self.root.winfo_rooty() + self.canvas.winfo_y()
            x1 = x0 + self.canvas.winfo_width()
            y1 = y0 + self.canvas.winfo_height()
            
            ImageGrab.grab(bbox=(x0, y0, x1, y1)).save(save_path)
            print(f"Canvas saved as {save_path}")
            
            # Reset the main window position
            self.root.geometry(f"+{window_x}+{window_y}")

    def exit_app(self):
        self.root.quit()
        self.root.destroy()

root = Tk()
app = TurtleApp(root)
root.mainloop()

Windows Removed

A while back I mentioned a return to Linux in a Dual Boot situation on my Laptop. The motivation here, primarily, is that it’s a perfectly good laptop, and Windows 10 is “end of life” and going away. Also, frankly, I miss using Linux more. I ran Linux on my last laptop before it was eventually replaced.

I’ve takent he next step and completely removed Windows.

I’ve done this for one reason, which is mayt not fix, but it might. I have, occasionally, been getting random system freezes. I never got this in Windows. My “theory” here is as follows:

  • My laptop has two physical drives, one is one of the newer M2 whatever drives that looks like a long ram stick chip, the other is an old school 3.5″ SSD. It seems really fucking weird to call an SSD “old school” like that, but whatever.
  • The SSD probably isn’t lose, but I have always felt like it fits a little loose inside the bay. I even have a piece of folded paper crammed inside to help give it some cushon to sty in place.
  • I am worried that the SSD is slipping out a bit and causing issues.
  • Also it’s possible the SSD is failing, but I doubt it.

Whatever the case, my ultimate goal was to move Linux to the original M2 drive and replace Windows, and I’ve completed that task. It was surprisingly painless, but not without hiccups. I was alos pretty nervous at first that I would hose up the laptop’s boot ability. As such, I wanted to give a general rundown of the process.

Just for a baseline of relevant points. With Windows my laptop:

  • Had 1 original M2 Drive with Windows on it, in Linux, this shows as “sdb”
  • Ad added SSD with 2 partitions, one with the Linux Mint file system, “sda3”
  • One Parition mapped as /home labeled “sda2”
  • A handful of other smaller patitions on both drives
  • Boot was set up for UEFI Enabled, Secure Boot Disabled

My first step was getting rid of the Windows File System. I simply opened “Disks” in Mint, and deleted the Windows Partition and the Recovery partition on sdb. This ended up being unecesary, but I did it anyway. Just for the sake of my sanity, I also rebooted to make sure I could still boot to Linux Mint. I also made a note that “sda” is 240GB and “sdb” is 256GB.

I then downloaded Clonezilla and write it to a USB drive to boot from. This ended up beingn the most complicated step. All of the tools I usually use to make writable USB drives from ISO files, Yumi, Rufus, Ventoy, all seem to only work from Windows. I also didn’t have my Yumi USB drive handy to just put the ISO there and boot.

I came across this command but it didn’t seem to actually boot.sudo dd bs=4M if=/path/to/file.iso of=/dev/sdX status=progress oflag=sync

It’s possible this did work, but I’ll touch at that later.

Ultimately, I discovered, that simply “right clicking” the .iso file, had a menu option to “Burn to Bootable USB”.

So I did that.

I also found that in order to boot from USB, I had to enter the BIOS, and turn off UEFI boot in favor of Legacy boot.

I booted to Clonezilla. I started to do a partition to partition clone but there didn’t seem to be an obvious way to clone sda3 (the file system) into the free/empty space of :”sdb”. So instead I just did a full disk clone of sda into sdb.

On reboot, I discovered that, I needed to reenable UEFI in order for things to work. After changing it back, I was in, Linux Mint booted just fine.

Next step was to clean up the duplicate partitions and resize each remainign parition to consume their respective drives. Actually my actual next step was to verify which copy of Mint I was running. I was going to drop some place holder test files, then reboot the machine and putz with the BIOS boot order. It turns out, I got lucky and I was already in the “new” copy on sdb. I was able to verify this because when I opebned Disks, the “old/original filesystem” partition, was not active. I simpley deleted it.

I also deleted the extra copy of “Home” that I had created on sdb.

Just for my own sanity, I did another reboot. Everything worked fine, and my home files mounted properly as expected. I now had the file system on the M2 drive sdb, and my home folder on the second drive of sda.

So now, I was ready to resize the partitions. Resizing the Homes drive was easy, I, once again, opened Disks, then did a resize and now it’s 240GB total.

The Filesystem was a bit less easy. Weirdly, I could, in the active file system, expand it to consime the 16GB or so at the “end” of the disk where the extra space now was (the new drive was 256GB vs the old SSD which was 240). What I couldn’t do was “pull it forward” to consime the 140GB or so that used to be the “home” partition.

So I went otu and downloaded a copy of Gparted this time, burned it to my USB stick, and rebooted. Now I was able to reize the file system to consume the entire remaining drive space.

This also meant toggling UEFI off and back on again.

Just for shits and giggles, I also decided to see what would happen if I enabled secure boot, which just entered into a weird boot loop. So I disabled that again, and finally, booted back into my now fully set up file system.

I can’t vouch for if it actually fixed my freeeze up issue yet. That may just be me over using it. I did also pick up some new memory for it, bumping it up to 16GB from 8GB, and also at a slightly faster clock speed.

Wrap-Up: Lets Try to Be Positive Edition

It’s been a hot minute since I really posted. To be blunt, I am pretty much just endlessly frustrated lately over the state of everything. Particularly the state of politics here in the US. I mean, I knew it would be shitty. I didn’t think it was going to be THIS relentlessly shitty, this quickly.

Lets take a peek inside the background at some journal topics here…

Or not. Lets try to, I dunno, be positive I guess. Whats been going on lately that is not awful.

Learning

Like last year, I started doing some leader-led training courses after work a bit. Its been, less rigorous than the ones last year, and mostly consisted of doing a Udemy course. This year’s topic so far has been AWS, or Amazon Web Services. Which probably powers like 75% of the internet or something. I am actually not sure why it was being offered because I am not sure we even use AWS at work, I think we mostly use Azure, but then, I am not part of that side of the business, so I actually have no clue what we use.

The Azure courses were full though.

As for AWS. The training specifically was to prep for the Amazon Cloud Practitioner certification. I probably should go take it. I am pretty confident I would pass it easily. The only part of the entire course I stumbled on was the weird, added later, 6-point diagram thingy. I can’t even remember the name of it. Like a lot of training of this nature, the whole course felt a bit like a sales pitch for AWS. But this particular section, which the class says was added later to the exam, felt very very very much like sale pitch nonsense.

Sales jargon and bull shit terms are my kryptonite. I can listen to and absorb some technical this and that about EC2 and S3 Blocks all day long. But this 6 points of whatever, fuck I just instantly glaze over. Don’t feed me “feel good” buzzword crap, please. Ever.

I also started doing a bit of Cybersecurity stuff (again) through some work access to something called Immersive Labs. I have no idea if its any good, so far I have just been doing basics, because it was required for work, but I seem to have full access, and it feels like something I should dig into more.

Back on the cert issue. I should go take the exam. I never did take the exams for CCNA or Pentesting for the courses I took last year. I don’t personally give a shit about certs. It shows you paid money to take a test somewhere. Feels very much in the vein of “feel-good jargon”.

Whatever the case, I have kind of wanted to better understand AWS in general for a while, and the course, overall did a great job of it. Most of it’s tools are well beyond anything I would probably ever need or use, but there were a few that were interesting.

First is the basic EC2. These are essentially their “on-demand servers.” These are not nearly as mysterious as I thought, and once you spin one up, you can log in and do all the normal Linuxey backend stuff one would expect. My thought was, of course, I could migrate my current web stack to AWS from Digital Ocean. I’m not sure I would really save anything for all that effort through. I do not have the need for some crazy highly flexible scalable environment. I’m not concerned about performance really. I’m just running a few WordPress instances against a basic database. At best, I would probably break even on my current spend for a bunch of migration work while supporting Amazon more, which I am already leaning more and more against.

Second was S3 Buckets. This is data storage in the cloud. I’ve considered using it for my backups a few times. It seems useful for some complex cloud app or systems. Even with Glacier storage, which is not on demand retrieval and is the cheapest, I would end up spending more than my current spend there. Right now I have a Microsoft Office 365 Family Plan and a bunch of segmented syncs off my NAS. That gives me 6 TB for around $70/year, AND Office for everyone in my family. That’s a great deal. Rough estimates in the calculator on Amazon, with only my current usage, puts me breaking even with Glacier.

Lastly, and the one I am more likely to use is Amazon Lambda. This one is really interesting, and it seems to just be, a way to run scripts int he cloud. I’m not sure I would use it a lot, but I could definitely see writing some simply monitoring/notification scripts and sticking them in there running once an hour or so. You get a ton of free runs per month too, which means it probably would cost me nothing int he end.

The Fediverse

Something that has stemmed from all the crazy stupid nonsense in the world is pushing more to move to use Federated social media more. I set up a Pixelfed account, which so far is mostly just reposted toy photos from IG. I have been poking around some Lemmy instances a bit. I am trying to use my Mastodon account a bit more. And BlueSky. Though that is less independent and federated.

Being more active in the Fediverse does not have to preclude not using mainstream social media. I do kind of plan to try to just, I dunno, focus less on just endless angry news. Not to say I am going to stop paying attention, just, more, add more attention elsewhere, also. Balance the awful with the good, or something.

I honestly just, hate what seems to be happening with Social Media. I hate that the mindset is that “Social media was a mistake” and “Facebook etc are evil.”. They really are, but also, it was not always this way. Social Media is good, and can be good, but they (social media companies) get too much out of making everyone fucking mad and miserable. I want to go back to giving a shit about family and friends on Facebook. I want to follow local news without brain-dead idiots filling the comments.

Oops, I am ranting a bit. Let’s be positive.

Language

I don’t have a ton to say about my language learning, but I did want to mention it. I feel like I have crossed some sort of threshold. I am not amazing at it, but I find lately I have a much much easier time understanding Spanish, and in a fairly passive way. I also was inspired a bit by a Reddit post, and have gotten way way more aggressive with lessons. There is a lot of repetition at the point I am.

A lot.

Muchos

I have been getting a little too bored with it. My current strategy is to finish the first bubble in a section, then do the first story, then skip ahead to the exam. I don’t need 5-6 more bubbles to prepare for that exam. I got it. This has actually been working pretty well. Especially because the repetition isn’t limited to within a single lesson section, it extends beyond to future lessons.

I am not doing it as aggressively as the Reddit post person did. They did one of these per day. I am doing one of these ever 2-3 days. I have done some other languages, but I have been working on this course for too long.

I also feel like I am retaining it better, because I am less bored.

Other

I have not been up to a lot much else. Like I said, the mess of everything just kind of saps my give a shit, which is frustrating. When I have not been doing class, I have been playing Infinity Nikki, or Fortnite, and we have been rewatching all the Marvel movies. Most of that is more Lameazoid topics.

I have avoided it (somehow) but everyone in my house has been sick for the past few weeks as well. Kind of sucks because it means the shop is closed. This is a slow time of year for that anyway, at least one of the other local businesses in the area there said they close for January usually anyway. Also, the bulk of the sales for the shop are on ebay anyway. The bulk of the space is for managing ebay items, the shop portion is just a nice “bonus”.

I’ve also been fitting in a lot of serious hardcore Bookmarks sorting. I kind of touched on this recently I think, I mentioned setting up Link Ace, which I have already dropped, for now. I’m just creating my big link list Digital Garden now instead. It works fine, it’s easily searchable. Another nice benefit is I am being reminded of a lot fo things I bookmarked to “look into later”. I have a nice sorted pile of ‘to-do projects” that I had completely forgotten about now. I can ignore them in a whole new way this way.’