Create mov,mp4 thumbnails from Python3


• July 31, 2018 • Leave a Comment

I was working on a Gtk 3 project to roll up all my iPhone videos into a flowbox for easy locating and processing since iPhone doesn’t name things in a meaningful way and photo managers dump things into not so meaningful folders, thumbnails was a must to locate videos easily. Plus, I wanted to do some Gtk programming.

I started with GStreamer 1.0, which worked, but not for .mov files. This handles MP4 and MOV. I have not tested other types so it might need further tweaks.

Hope it is helpful to someone. I can provide GStreamer code if you need it. It is also Python and is slightly faster.

#!/usr/bin/python3
import os
import time
import datetime
import getpass
import cv2
from PIL import Image

"""
Requires:
    OpenCV2 
    PIL

Install via pip or apt.
"""


def get_framecv(in_vid, out_vid, w=192, h=108):
    """Generate thumbnail."""
    # Opencv create thumb.
    try:
        cap = cv2.VideoCapture(in_vid)
        video_length = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) - 1
        print('length',video_length)
        while cap.isOpened():
            try:
                ret, frame = cap.read()
                cv2.imwrite(out_vid, frame)
                break
            except:
                pass
        cap.release()
        # PIL: resize
        img = Image.open(out_vid) # image extension *.png,*.jpg
        img = img.resize((w, h), Image.ANTIALIAS)
        img.save(out_vid)
    except Exception as e:
        return e

def is_video_file(filename, extensions):
    """Filter videos"""
    return any(filename.lower().endswith(e) for e in extensions)

def scan_vids(base_dir, extensions):
    """Walk path and get all videos in path."""
    for dirpath, dirs, files in os.walk(base_dir):
        for filename in files:
            if is_video_file(filename, extensions):
                full_path = os.path.join(dirpath,filename)
                date_object = datetime.datetime.fromtimestamp(os.path.getctime(full_path))
                yield os.path.join(dirpath,filename),date_object.date()

def gen_thumbs(path,extensions,output=None):
    """Worker function"""
    img_date = datetime.datetime.now().date()
    associations = []

    if output:
        img_store = output
    else:
        #img_store = r'/home/%s/video_thumbs' % getpass.getuser()
        img_store = os.path.join(path, 'thumbnails')

    if not os.path.exists(img_store):
        os.mkdir(img_store)

    for vid_info in scan_vids(path, extensions):
        vid_path,vid_date = vid_info
        vid_name = os.path.split(vid_path)[-1].split('.')[0]
        img_path = os.path.join(img_store,'thumb_%s.png' % vid_name)
        err = get_framecv(vid_path,img_path)

        if err:
            continue # Handle errors.

        associations.append((img_path, img_date, vid_path, vid_date))

    return associations


if __name__ == '__main__':

    filter_extensions = ['.mov', '.mp4']
    local_path = None # Set path to run from program.

    if local_path:
        user_path = local_path
    else:
        # Run from terminal.
        user_path = None

        user_path = input('Path to videos: ')

    if not user_path:
        print('Path required.')
        os._exit(0)
    if not os.path.exists(user_path):
        print('Supplied path %s is invalid.' % user_path)
        os._exit(0)

    # Run...
    results = gen_thumbs(user_path, filter_extensions, output=None)
    # Print results.
    for i in results: print(i)

GTK3 Calendar dialog example in Python.


• July 23, 2018 • Leave a Comment

 

#!/usr/bin/python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gio, GObject
GObject.threads_init()

class CalDialog(Gtk.Dialog):
    '''
    Calendar Dialog
    '''
    def __init__(self, parent):
        Gtk.Dialog.__init__(self, "Select Date", parent, 0,
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
             Gtk.STOCK_OK, Gtk.ResponseType.OK))

        self.set_default_size(300, 200)

        self.value = None

        box = self.get_content_area()

        calendar = Gtk.Calendar()
        calendar.set_detail_height_rows(1)
        calendar.set_property("show-details",True)
        calendar.set_detail_func(self.cal_entry)

        box.add(calendar)

        self.show_all()

    def cal_entry(self, calendar, year, month, date):
        #print(year, month, date)
        self.value = calendar.get_date()

class FlowBoxWindow(Gtk.Window):
    '''
    Flowbox example mixed with HeaderBar from
    https://python-gtk-3-tutorial.readthedocs.io/en/latest/introduction.html
    '''
    def __init__(self):
        Gtk.Window.__init__(self, title="Calendar Demo")
        self.flowbox = None
        self.set_border_width(10)
        self.set_default_size(400, 250)

        header = Gtk.HeaderBar(title="Calendar Demo")
        header.props.show_close_button = True

        # Add button to header to display calendar dialog.
        cal_button = Gtk.Button()
        icon_cal = Gio.ThemedIcon(name="gnome-calendar")
        image_cal = Gtk.Image.new_from_gicon(icon_cal, Gtk.IconSize.BUTTON)
        cal_button.add(image_cal)
        cal_button.set_tooltip_text("Pick date")
        cal_button.connect("clicked", self.on_cal_clicked)
        header.pack_start(cal_button)

        # Button to exit main window.
        exit_button = Gtk.Button()
        icon_exit = Gio.ThemedIcon(name="exit")
        image_exit = Gtk.Image.new_from_gicon(icon_exit, Gtk.IconSize.BUTTON)
        exit_button.add(image_exit)
        exit_button.set_tooltip_text("Exit")
        exit_button.connect("clicked", self.on_exit_clicked)
        header.pack_start(exit_button)

        self.set_titlebar(header)

        self.flowbox = Gtk.FlowBox()
        self.flowbox.set_valign(Gtk.Align.START)
        self.flowbox.set_max_children_per_line(30)
        self.flowbox.set_selection_mode(Gtk.SelectionMode.NONE)
        self.flowbox.set_property("vexpand", False)

        self.show_all()

    def on_cal_clicked(self, widget):
        # Open calender and get user selection,
        dialog = CalDialog(self)
        response = dialog.run()
        if response == Gtk.ResponseType.OK:
            print(dialog.value) # See terminal.
        # Close calendar.
        dialog.destroy()

    def on_exit_clicked(self, widget):
        self.destroy()

win = FlowBoxWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

System 76 Oryx Pro Host Virtualization


• July 10, 2018 • Leave a Comment

Preview of OS X and Windows 10 virtualized on a June 2018 Oryx Pro laptop.


1920×1080 MP4

Brightness fix on System 76 Oryx Pro


• July 8, 2018 • Leave a Comment

I recently received the System 76 Oryx Pro laptop – June 2018 – and this thing is a beast. If you want more portable go with something else. I decided to get it preinstalled with Pop! OS to see how well it integrated with the hardware. By default most things worked and I really didn’t spend much time configuring or fixing things beyond setting it up for me and my workflow. I did install Gnome Tweaks to alter a couple of things but for the most part the OS is unchanged.

To the problem. Out of the box there was no ability to control brightness on the laptop monitor. I do not use external monitors so I do not know if this will fix anything related to that. To partially fix brightness when using Nvidia in Pop! OS I simply flashed the BIOs and it magically started working. The brightness controls/keys worked in Pop but it stops working after the lid is closed or the machine goes to sleep. This did not work for Nvidia in Mint 19 at all.

Update: The following grub hack does not work! None do, unless you want to install utils and write some scripts to hack things. What I found that does work is to install Linux Mint 19, much nicer OS IMO but this likely works in any Gnome DE, and install this. The control in power settings wont work, but this does and adds color too, but only if you switch to Intel drivers. I assume Intel will work better in Pop too. I also had problems with the System 76 drivers in Mint so install at your own risk. I’m hoping a future Nvidia driver/kernel update will correct the problem at some point.

If you plan to install Windows this laptop is a rebranded Sager Gaming laptop so visit their site for ease of driver lookups. I think it’s a Clevo P650SE.

The second problem was after putting the laptop to sleep/suspend (closing the lid) the brightness controls would stop working until it was rebooted. Irritating! Here’s the fix:

1. Verify the Nvidia driver is installed and in use.
2. Update BIOs firmware if screen brightness slider missing and keyboard keys are not working.
3. Edit the /etc/default/grub file from the terminal.
a. sudo nano /etc/default/grub
b. Change GRUB_CMDLINE_LINUX_DEFAULT from “quiet splash” to “quiet splash acpi_osi=Linux”
c. sudo update-grub
d.reboot

Apple is a shit hole company


• August 22, 2017 • Comments Off on Apple is a shit hole company

I went to upgrade to El Capitan on my MacBook Pro (17-inch, Mid 2009). I added max ram and a solid state drive. I have no problems virtualizing Windows and Linux on this fucker, but Apple decided my 17″ MBP is no longer viable and will not let me upgrade. Yes, I know I can without them, but fuck that! This is outright bullshit! So I went to run XCode, which I hadn’t ran since the last update – I develop more in Windows or Linux in Python or Rust than OS X in XCode – and they broke it. So I go to the AppBore to upgrade and it just spins, so I am not allowed to update XCode either? I am now forced to hack Sierra onto the machine, install Linux and virtualize OS X, or buy a new laptop with a smaller fucking screen. FUCK YOU! I am buying a System76 powerhouse, and maybe a Puri.sm with the money I save, and virtualizing their crap. Either way, I wrote my two cents on the AppStore and of course they will not allow it to go through so here it is:

“I cannot update XCode, because Apple decided that the over priced hardware I purchased, that was 6 months behind other companies when it was new, is not viable for an OS upgrade from El Capitan to Sierra. I run Windows and Linux virtualized on this laptop as well as play games with no issue. Not only that but they broke the XCode I currently have installed, which worked fine until the last update. The reality, the company changes your system preferences without permission, the majority of AppStore apps are horrid, they kill apps in the store often or steal the ideas effectively killing the original app, and ask for way more for older hardware than required to blackmail you into buying new hardware just to run a single OS version above what you’re running already. They are not a small company that needs to command a higher cost to maintain their business or need to fill landfills with viable hardware to keep money rolling in. The iPhone is great but as a company they are worse than Microsoft in the nineties. I’m going to System76 and LinuxMint and I will virtualize OS X from now on. Not worth my time or money anymore…”

Now don’t give me that, “they are protecting you”, “they want to be secure”, “they need to advance” bullshit. If the motherfuckers are using open Unix shit to build off of, don’t fuck it up, don’t mess with my settings, allow good programs into the AppStore, allow Linux and/or open devs a good environment to build in, and stop trying to be an everything greedy business. If Windows was better I’d go back to that as Microsoft has at least wised up a little bit.

Really nice set of free javascript canvas gauges.


• February 5, 2017 • Comments Off on Really nice set of free javascript canvas gauges.

https://canvas-gauges.com

Krita is becoming a Painter and Photoshop killer.


• January 9, 2017 • Comments Off on Krita is becoming a Painter and Photoshop killer.

It has been a long time since I checked out Krita. I Painted this in Krita 3.1.1 on Mac Yosemite, versions for Windows as well and of course Linux; all are free. The bird took about six hours, give or take, and the program was not shut down for two days. I had one lock up during export of the final PNG save, no problems with the tools or app other than that. Be sure to save often, just in case. I used the following brushes, available here:

David Revoy
Concept Art & Illustration Pack
Cazu Brush Collection
Raghukamath

Just playing around I created this in no time:
Background
Further playing ended with this:
Background
Of course this gave me an excuse to continue painting:
Background
and finally, the end result:
Background

Linux like Package Management for Windows and Mac


• January 8, 2017 • Comments Off on Linux like Package Management for Windows and Mac

Package managers for installing open software. These gives you Linux like package management similar to Linux apt, zypper, yum, etc…

Windows:

https://chocolatey.org/install

Mac:

http://brew.sh/

Brew GUI

https://www.cakebrew.com/

Convert PostScript to PDF from D lang calling GhostScript ps2pdf.


• September 9, 2016 • Comments Off on Convert PostScript to PDF from D lang calling GhostScript ps2pdf.

I’m learning D. This provides working examples of getopts, concurrency, and a process call.

import std.getopt;
import std.stdio; 
import std.file;
import std.path;
import std.datetime;
import std.algorithm;
import std.array;
import std.conv;
import std.string;
import std.process;
import std.concurrency;

string[] listdir(string path)
{
    return dirEntries(path, "*.ps", SpanMode.shallow)
        .filter!(f => f.isFile)
        .map!(f => buildPath(path, f.name))
        .array;
}

void rip(string path, bool v=false) 
{
	//ps2pdf filename.ps filename.pdf
	string outpath = path.replace(".ps", ".pdf");
	if (v == true) {
		writefln("OUT: %s", outpath);
	}
	auto logFile = File("errors.log", "w");
	auto pid = spawnProcess(["/usr/bin/ps2pdf", path, outpath],
                        std.stdio.stdin,
                        std.stdio.stdout,
                        logFile);
	wait(pid);
}

void main(string[] arguments)
{

	// Get opts
	auto base_path = "/home/username/Documents/postscript";
    bool render = false;
    bool count = false;
    bool verbose = false;
    
	getopt(
		arguments,
		"p|path", &base_path,
		"r|render", &render,
		"c|count", &count,
		"v|verbose", &verbose
	); 
	
	// $./psmerge -p /home/username/Documents/postscript -r -c -v
	StopWatch sw;
	sw.start();
	
	int totalDocs = 0;
	int totalPages = 0;
	if (base_path.exists) {
		
		string[] psfiles = listdir(base_path);
		foreach(ps; psfiles){
			
			if (render == true) {

				auto tid = spawn(&rip, ps, verbose);
				
				if (verbose == true){
					writeln(tid, " \nIN: ", ps);
				}
			}
			
			totalDocs++;
			
			// Count pages.
			if (count == true) {
				// Read file.
				File fileHandle = File(ps, "r");
			   
				while (!fileHandle.eof()) 
				{
					string line = fileHandle.readln();
					if ( canFind(line, "(atend)") ) {
						continue;
					}
					if ( canFind(line, "%%Pages: ") ) {
						
						string[] s = line.split(":");
						auto pgNum = strip(s[1].replace("\r\n",""));
						
						if (verbose == true){
							writeln("Pages: ", pgNum);
						}
						
						auto pgCount = to!long(pgNum);
						totalPages += pgCount;
					}
				}
			}
		}
		writefln("Total Docs: %s", totalDocs);
		writefln("Total Pages: %s", totalPages);
		
		sw.stop();
		writefln("Elapsed: %f seconds", sw.peek().usecs / 1_000_000.0);
	}
}

Gambas 3 TreeView and TabStrip Example Code


• September 3, 2016 • Comments Off on Gambas 3 TreeView and TabStrip Example Code

I sometimes play around with Xojo and PureBasic for quick and simple GUI apps since the interface designers are convenient and the language makes for easy coding. I was checking out Gambas 3 and found it to be fairly good but lacking in a lot of examples. I was messing around with TreeView and TabStrip working together. It took a bit and I am sure there are better ways to approach this, but if you need examples of either of these controls this is working code.

I have a form with an HSplit. If the left side I have a TreeView and the right I have a TabStrip. I left names default and made the object expand to the window. I still haven’t figured out how to resize either side of the HStrip. This just loads some books off the file system to populate the treeview and creates a tab when clicked. I also added tab delete buttons. I have a module for the base path, but you can set it in form open. Screen shot here.

At this point I am thinking I will need to subclass and make my own form objects as this was a pain in the ass. Maybe this will help someone.

Public Sub TabStrip1_Close(idx As Integer)
  Try TabStrip1[idx].Text = Null
    TabStrip1[idx].Delete()
  Catch
    TabStrip1[idx].Text = Null
    TabStrip1.Closable = False
End

Public Sub TabStrip1_Click()
  Dim k As String
  If Len(TabStrip1[TabStrip1.Index].Text) > 0 Then
    Message(TabStrip1[TabStrip1.Index].Text)
  Endif
End

Public Sub TreeView1_Click()

  Dim counter As Integer

  If TreeView1.Item.Key Begins "root_" Or TreeView1.Item.Key Begins "parent_" Then
    'Do nothing For parent items or capture parent events.
    Return
  Endif
  
  If TreeView1.Item.Children = 0 Then
    
    If TabStrip1.Index = 0 And Len(TabStrip1[TabStrip1.Index].Text) = 0 Then
      If Len(TreeView1.Item.Text) > 0 Then
        If Len(TreeView1.Item.Text) > 0 Then
          TabStrip1[TabStrip1.Index].Text = TreeView1.Item.Text
          TabStrip1.Closable = True
          TabStrip1.Count = 1
        Endif
      Endif
    Endif
    
    For counter = 0 To TabStrip1.Count - 1
      If TreeView1.Item.Text = TabStrip1[counter].Text Then
        'Focus tree item on corresponding tab select.
        TabStrip1.Index = counter
      Endif
    Next

    If TreeView1.Item.Text = TabStrip1[TabStrip1.Index].Text Then
      Return
    Else
      'Add new tab if not in tabs.
      TabStrip1.Count += 1
      TabStrip1[TabStrip1.Index].Text = TreeView1.Item.Text
    Endif
  Endif
End


Public Sub Form_Open()

  Dim File As String
  Dim path_parts As String[]
  Dim file_parts As String[]
  Dim cat As String
  Dim cat_key As String
  Dim title As String
  Dim treeIndex As Integer
  
  'Dim img_book As New Picture(16, 16, False)
  'Dim img_cat As New Picture(16, 16, False)
  'Dim img_root As New Picture(16, 16, False)
  'img_book.Load("book.png")
  'img_cat.Load("cat.png")
  'img_root.Load("root.png")
  
  'Globals.basePath = "~/ebooks"
  If Not Exist(globals.basePath) Then
    Mkdir globals.basePath
  Else
    'TreeView1.Add("root_EBooks", "EBooks", img_root) 'With icon.
    TreeView1.Add("root_EBooks", "EBooks")
    For Each File In RDir(globals.basePath, "*.epub").Sort()
      
      path_parts = Split(File, "/")
      cat = path_parts[0]
      cat_key = "parent_" & cat
      file_parts = Split(path_parts[1], ".")
      title = file_parts[0]
      
      If Not TreeView1.Exist(cat_key) Then
        TreeView1.Add(cat_key, cat,, "root_EBooks")
      Endif
      
      treeIndex += 1
      TreeView1.Add(title, title,, cat_key)
    Next
  Endif

End