Tag Archives: programming

Melting effect in 8086, using only register manipulation

(NOTE, Dosbox can’t cope with the register speed, use real HW or PCem)

Effect using a edited photo I made from fireworks ..

Generating a RAW image and Palette, a in a new way

This bash script to convert BMP to Raw and a compiled colorpalette.
(Note: this converts to 8 bit depth, the assembly code in the final assemby program converts to 6 for VGA mode 13h

So this time, i won´t have to use the standard VGA palette as mentioned in previous posts.
(Gimp colors > indexed (255 colors) ; save as BMP, exclude colorspace information)

I’m using identify to extract the colorpalette, which i’m converting to DB entries for the fasm compiler

#!/bin/bash
if [ $# -lt 1 ] ; then
	echo "$0 filename"
	exit 0
fi
size=$(stat $1 | grep Size | awk '{ print $2 }')
skipsize=$(( $size - 64000))
dd if=$1 of=$1.raw skip=$skipsize bs=1

identify -verbose $1 | awk '/Colormap:/,/Rendering/' | grep -v Colormap | grep -v Rendering | awk '{ print $2 } ' | tr -d '()' | while read ; do echo "db $REPLY" ;done > data.asm
fasm data.asm

Code

use16
org 0x100

; variables
CRTC_INDEX = 0x03D4
CRTC_DATA = 0x03D5
INPUT_STATUS = 0x03DA
HRETRACE = 0x01
VRETRACE = 0x03 ; bit 3 =8 ?
MAXIMUM_SCAN_LINE = 0x09
LINE_OFFSET = 0x13

; bar
upperbar = 1
lowerbar = 399

jmp start
; memory locations for data
updown dw 1
direction DB 0
filename DB "firework.raw",0
oldline db 0


start:
; set mode 320x200 256 colors palette
	mov ah,0x0
	mov al,13h
	int 10h

; clear screen routine, not really needed
clearscreen:
	push ax
	mov ax, 0a000h
	mov es, ax
	pop ax
	xor di, di
	inc ax
	mov cx, 64000 ; 320x200
	rep stosb
; set colors
; call file loader 
	call Loadfile
	call setpalette

; Move loaded file to Screen memory
	mov ax,0a000h
	mov es,ax
	mov ax,6000h
	mov ds,ax
	mov si,0
	mov di,0
	mov cx,320*200/2
	rep movsw

	push cs
	pop ds

;	store org effect2 values
	mov dx, CRTC_INDEX
	mov al,LINE_OFFSET
	out dx,al
	mov dx, CRTC_DATA
	in al,dx
	mov [oldline],al


; after displaying the image or displaying an error, wait for keypress to exit
waitforkeyloop:
	call effect 	; Calling the effect
	MOV AH,1
	INT 16h
	JZ waitforkeyloop
	XOR AH,AH
	INT 16h
Exit:
	MOV AX,3	; default text mode 3
	INT 10h
	MOV AX,4C00h	; exit to dos (terminate process)
	INT 21h
; loop ends here

; Loads raw 64000 bytes image to screen memory
Loadfile:
	push ds
	MOV DX,filename
	MOV AX,3D00h	; open filehandle
	INT 21h
	JC Err1
	MOV BX,AX   	; filehandle
	MOV CX,64000
	mov dx,06000h 	; destination 0000:a000h - Screen memory
	mov ds,dx
	MOV DX,0
	MOV AH,3Fh	; read from file
	INT 21h
	JC  Err1
	MOV AH,3Eh	; close filehandle
	INT 21h
	pop ds
RET

; print error
Err1:
	push cs		; make ds same as cs
	pop ds
	MOV AX,3	; default text mode 3
	INT 10h
	MOV DX,TxtErr1	; error
	MOV AH,09h
	INT 21h
	RET

effect:
	cli		; stop interrupts
	call waitvretrace	; wait for vertical retrace

; gets start scanline and direction
	mov ax,[updown]
	mov cl,[direction]
	cmp cl,0		; 0 move down
	jz	addcounter
	dec ax
	dec ax
	cmp ax,upperbar  	; reached upper bar ?
	jnz gohere ; jnz
	mov cl,0
	mov [direction],cl
	jmp gohere
addcounter:
	inc ax
	inc ax
	cmp ax,lowerbar		; reached bottom bar?
	jnz gohere 	;jnz
	mov cl,1		; change direction
	mov [direction],cl
gohere:
	mov [updown],ax		; store new location

; al = scanline, call wait for scanline
	call longwaithretrace
; other effect
        mov dx, CRTC_INDEX
        mov al, LINE_OFFSET
        out dx,al
        mov dx, CRTC_DATA
        mov al, 0
        out dx,al


; wait scanlines (height of bar)
	mov ax,400
	mov cx,[updown]
	sub ax,cx
	call longwaithretrace

; restore effect2
        mov dx, CRTC_INDEX
        mov al, LINE_OFFSET
        out dx, al
        mov dx, CRTC_DATA
        mov al, [oldline]
        out dx,al
		
	sti	; start interrupts again
	ret

; routine that fixes 8 to 6 bits and sets palette
setpalette:
	; 8 bits to 6 
	mov si,coltab
	mov cx,256*3
	rest:
		mov al,[si]
		shr al,2
		mov [si],al
		dec cx
		inc si
		cmp cx,0
		jnz rest
; now set colors
	mov dx,3c8h
	xor al,al
	out dx,al
	inc dx
	mov si,coltab
	mov cx,256*3
	rep outsb
ret


; this waits for vertical retrace
waitvretrace:
	mov dx,INPUT_STATUS
	waitv1:
		in al,dx
		test al,8
		jnz waitv1
	waitv2:
		in al,dx
		test al,8
		jz waitv2
ret

; routine that waits for horizontal retrace
waithretrace:
	mov cl,al
	mov dx,INPUT_STATUS
	waith1:
		in al,dx
		test al,1
		jnz waith1
	waith2:
		in al,dx
		test al,1
		jz waith2
		dec cl
		cmp cl,0
		jnz waith1
ret

longwaithretrace:
	mov cx,ax
	mov dx,INPUT_STATUS
	lwaith1:
		in al,dx
		test al,1
		jnz lwaith1
	lwaith2:
		in al,dx
		test al,1
		jz lwaith2
		dec cx
		cmp cx,0
		jnz lwaith1
ret
TxtErr1 DB "firework.raw not found!",7,10,13,"$"

coltab: 
include 'data.asm'

Rasterbar Copperbar line short code

Not interesting for most of you, but here is the minimal code to display a line using toggling the background color at a specific retrace.

https://www.henriaanstoot.nl/2023/09/12/copperbar-effect-with-image-on-80×86/

3C8h (R/W):  DAC Address Write Mode
 bit 0-7  The color data register (0..255) to be written to 3C9h.
 Note: After writing the 3 bytes at 3C9h this register will increment, pointing to the next data register.

3C9h (R/W):  DAC Data Register
 bit 0-8?  Color value
 Note:  Each read or write of this register will cycle through first the
        registers for Red, Blue and Green, then increment the appropriate
        address register, thus the entire palette can be loaded by writing 0 to
        the DAC Address Write Mode register 3C8h and then writing all 768 bytes
        of the palette to this register.

3DAh
Input Status #1 Register (Read at 3BAh (mono) or 3DAh (color))
7	6	5	4	3	2	1	0
                            VRetrace			DD
 
VRetrace -- Vertical Retrace
"When set to 1, this bit indicates that the display is in a vertical retrace interval.This bit can be programmed, through the Vertical Retrace End register, to generate an interrupt at the start of the vertical retrace."
DD -- Display Disabled
"When set to 1, this bit indicates a horizontal or vertical retrace interval. This bit is the real-time status of the inverted 'display enable' signal. Programs have used this status bit to restrict screen updates to the inactive display intervals in order to reduce screen flicker. The video subsystem is designed to eliminate this software requirement; screen updates may be made at any time without screen degradation."

Code (fasm)

use16
org 0x100

INPUT_STATUS = 0x03DA

start:

; set mode 320x200 256 colors palette
	mov ah,0x0
	mov al,13h
	int 10h

; press key to exit
waitforkeyloop:
	call effect 	; Calling the effect
	MOV AH,1
	INT 16h
	JZ waitforkeyloop
	XOR AH,AH
	INT 16h
Exit:
	MOV AX,3	; default text mode 3
	INT 10h
	MOV AX,4C00h	; exit to dos (terminate process)
	INT 21h

effect:
	cli		; stop interrupts
	call waitvretrace	; wait for vertical retrace
	mov al, 0    ; set color index 0 to black (needs to be converted to a function
	mov dx, 3c8h
	out dx, al
	inc dx       ; now 3c9h
	mov al, 0h
	out dx, al   ; set R = 0
	mov al, 0h
	out dx, al   ; set G = 0
	mov al, 0h
	out dx, al   ; set B = 0

	mov al,30h

; al = scanline, call wait for scanline
	call waithretrace
	mov al, 0    ; set color index 0 to white
	mov dx, 3c8h
	out dx, al
	inc dx       
	mov al, 255
	out dx, al   
	mov al, 255
	out dx, al   
	mov al, 255
	out dx, al   

; wait 1 scanlines (height of bar)
	mov al,1h
	call waithretrace

; draw black again
	mov al, 0    ; set color index 0's rgb value
	mov dx, 3c8h
	out dx, al
	inc dx       ; now 3c9h
	mov al, 0
	out dx, al   
	out dx, al   
	out dx, al   
		
	sti	; start interrupts again
	ret

; this waits for vertical retrace
waitvretrace:
	mov dx,INPUT_STATUS
waitv1:
	in al,dx
	test al,8
	jnz waitv1
waitv2:
	in al,dx
	test al,8
	jz waitv2
	ret

; routine that waits for horizontal retrace
; al sets number of retraces
waithretrace:
	mov cl,al
	mov dx,INPUT_STATUS
waith1:
	in al,dx
	test al,1
	jnz waith1
waith2:
	in al,dx
	test al,1
	jz waith2
	dec cl
	cmp cl,0
	jnz waith1
	ret

My bash file to copy com file to floppy image to use in PCem.

PCem right button disk change drive A:

fasm one-line.asm
# disk.img is een msdos boot floppy image
sudo mount -o loop disk.img mountpoint
sudo cp *com mountpoint/
sudo cp *bmp mountpoint/
sudo umount mountpoint

Busy day: PHP, Python, RP2040 and Frequency detection.

While watching a online python course, I was writing the code for a music guessing game (Highland Bagpipe Tunes)
The core is working, now it’s a matter of filling this “pruts” with tunes.

Switching between python, php, bash and C is a nightmare 🙂

A screenshot of work in progress

Then the postman came .. with goodies.
I needed the MAX9814 analog microphone with amplifier, all of my other sensors were not up to the task.

So I switched to this WIP with the MAX9814.
I want to make a little gadget using an Arduino and 9 leds, which uses FFT to blink which note I am playing on my Highland Pipes.

So detecting is working, now I have to attach a bunch of leds.

First test using Arduino Cloud (I still prefer PlatformIO) But this is better than the old IDE. (Note, you have to install an agent to connect your browser to a board)

Next thing I did today:
Getting my waveshare RP-2040 Zero working with micropython.

Great the little NeoPixel Led on the board.

Steps to get this working:

  • Install Thonny
  • Connect the rp2040 via USB with the boot button pressed
  • place RPI_pico.xxxx.uf2 on the mounted usb disk, it will reboot
  • Run Thonny connect and run a test program

Want to run program @boot ?
save -> to device, and call main.py

Micro Adlib player in Assembly

Plays RAW adlib songs in 100 lines of code … kindda

Using information from here:
https://moddingwiki.shikadi.net/wiki/RAW_Format_(Adlib)

And using fasm to compile I can play captured raw songs.

But something is still off ?!?

It sounds a little different, and I need to implement a better timer routine. (Below my version and opencubicplayer)

This is a test for my bootloader, playing music from my bootblock!

CODE

use16
org 0x100

ctrlreg=0388h
datareg=0389h


mainloop:
	; set speed (Byte 8,9 from the raw file)
	mov ax,[tune+8]
	mov [clockspeed],ax
	; call player
	call rawreg
	; wait 0.5 sec for exit
	mov bx,6
	call waitmore
	jmp exit

rawreg:
	mov bx,tune+0ah		; start of song at offset ah

; order registerdata, register!
; Are there more control codes? ???

lraw:
	mov cx,[bx]
; reg = 2 - check data
	cmp ch,2
	je checkreg2
; reg = 0 - cyclewait
	cmp ch,0
	je cyclewait
; data = FFFF - end song - end play routine
	cmp cx,0ffffh
	jne skipr
	ret

cyclewait:		; waits cl times waitroutine
cylloop:	
	call waitlong
	dec cl
	jnz cylloop
	inc bx
	inc bx
	jmp lraw

checkreg2:
; check low opl
	cmp cl,1
	jne checkh
	mov ch,0
	mov [highlow],ch
	jmp incandret
checkh:
; check high opl
	cmp cl,2
	jne check00
	mov ch,1
	mov [highlow],ch
	jmp incandret
check00:
; set new speed
	cmp cl,0
	jne incandret
	inc bx
	inc bx
	mov ax,[bx]
	mov [clockspeed],ax
	
incandret:
; next double byte in the song please
	inc bx     
	inc bx     
	jmp lraw

skipr:
; sends data to the control and data registers
	mov dx,ctrlreg
	mov al,[highlow]
	cmp al,0
	je regokay
	inc dx
	inc dx
regokay:
	mov al,ch
	out dx,al
;	call waitshort ; not needed for newer adlib cards
	mov dx,datareg
	mov al,[highlow]
	cmp al,0
	je regokay2
	inc dx
	inc dx
regokay2:
	mov al,cl
	out dx,al
	call waitlong
	inc bx
	inc bx
	jmp lraw

waitshort:
	push ax
	push cx
	push dx
	mov cx, 0      ;HIGH WORD.
	mov dx, 010h ;LOW WORD.
	mov ah, 86h    ;WAIT.
	int 15h
	pop dx
	pop cx
	pop ax
	ret

waitlong:
	push bx
	push ax
	push cx
	push dx
	mov cx, 0      ;HIGH WORD.
	mov dx, [clockspeed]
	shr dx,1
	mov ah, 86h    ;WAIT.
	int 15h
	pop dx
	pop cx
	pop ax
	pop bx
	ret

waitmore:
; in bx == 12h is 1 sec
; destroys ax,bx,cx,dx
	push ax
	push bx
	push cx
	push dx
	mov ax,0h
	int 1ah
	add dx, bx
	mov bx,dx
waitloop:
	mov ax,0h
	int 1ah
	cmp bx,dx
	jnz waitloop
	pop dx
	pop cx
	pop bx
	pop ax
	ret

exit:
	mov dx,0388h
	mov al,0b0h
	out dx,al
	inc dx
	xor al,al
	out dx,al
	mov ax,04c00h
	int 21h

clockspeed: dw 0

highlow:	db 0

tune:
	file 'RAWSONG.RAW'

Copperbar effect with image on 80×86

I’m still having problems getting a working floppy drive in my machine.
(Broken FDD card, drive errors etc)

The raster bar (also referred to as rasterbar or copperbar) is an effect used in demos and older video games that displays animated bars of colour, usually horizontal, which additionally might extend into the border, a.k.a. the otherwise unalterable area (assuming no overscan) of the display

When you look at the left side of the screen you see the color bar in the border (outside the normal pixel screen)

I first tried to get it working in DosBOX, but thats a mess.
Good for simple emulation but not hardcore register manipulation.

Below dosbox

Three examples below are in PCem

Not waiting for vsync, gives some idea how much timing is left when doing bars
Other effect added
Effect as on the real hardware except emulated using PCeM
use16
org 0x100

CRTC_INDEX = 0x03D4
CRTC_DATA = 0x03D5
INPUT_STATUS = 0x03DA
MAXIMUM_SCAN_LINE = 0x09
LINE_OFFSET = 0x13

jmp start

updown DB 30
direction DB 0
filename DB "shoes.bmp",0

start:
; set mode 320x200 256 colors palette
        mov ah,0x0
        mov al,13h
        int 10h

; clear screen routine, not really needed
clearscreen:
        push ax
        mov ax, 0a000h
        mov es, ax
        pop ax
        xor di, di
        inc ax
        mov cx, 64000 ; 320x200
        rep stosb

; call file loader
        call Loadfile

        push cs
        pop ds

; after displaying the image or displaying an error, wait for keypress to exit
waitforkeyloop:
        call effect     ; Calling the effect
        MOV AH,1
        INT 16h
        JZ waitforkeyloop
        XOR AH,AH
        INT 16h
Exit:
        MOV AX,3        ; default text mode 3
        INT 10h
        MOV AX,4C00h    ; exit to dos (terminate process)
        INT 21h

Loadfile:
        MOV DX,filename
        MOV AX,3D00h    ; open filehandle
        INT 21h
        JC Err1
        MOV BX,AX       ; filehandle
        MOV CX,0FFFFh   ; size
        mov dx,0a000h   ; destination 0000:a000h - Screen memory
        mov ds,dx

        MOV DX,0
        MOV AH,3Fh      ; read from file
        INT 21h
        JC  Err1
        MOV AH,3Eh      ; close filehandle
        INT 21h

        RET
; print error
Err1:
        push cs         ; make ds same as cs
        pop ds
        MOV DX,TxtErr1  ; error
        MOV AH,09h
        INT 21h
        RET

effect:
        cli             ; stop interrupts
        call waitvretrace       ; wait for vertical retrace
        mov al, 0    ; set color index 0 to black (needs to be converted to a function
        mov dx, 3c8h
        out dx, al
        inc dx       ; now 3c9h
        mov al, 0h
        out dx, al   ; set R = 0
        mov al, 0h
        out dx, al   ; set G = 0
        mov al, 0h
        out dx, al   ; set B = 0

; gets start scanline and direction
        mov al,[updown]
        mov ah,[direction]
        cmp ah,0
        jz      addcounter
        dec al
        cmp al,30
        jnz gohere
        mov ah,0
        mov [direction],ah
        jmp gohere
addcounter:
        inc al
        cmp al,100
        jnz gohere
        mov ah,1
        mov [direction],ah
gohere:
        mov [updown],al

; al = scanline, call wait for scanline
        call waithretrace
        mov al, 0    ; set color index 0 to blueish
        mov dx, 3c8h
        out dx, al
        inc dx
        mov al, 11h
        out dx, al
        mov al, 22h
        out dx, al
        mov al, 33h
        out dx, al
; wait 10 scanlines (height of bar)
        mov al,10
        call waithretrace

; draw black again
        mov al, 0    ; set color index 0's rgb value
        mov dx, 3c8h
        out dx, al
        inc dx       ; now 3c9h
        mov al, 0
        out dx, al   ; set R = 11h
        mov al, 0h
        out dx, al   ; set G = 22h
        mov al, 0h
        out dx, al   ; set B = 33h

        sti     ; start interrupts again
        ret

; this waits for vertical retrace
waitvretrace:
        mov dx,INPUT_STATUS
waitv1:
        in al,dx
        test al,8
        jnz waitv1
waitv2:
        in al,dx
        test al,8
        jz waitv2
        ret

; routine that waits for horizontal retrace
waithretrace:
        mov cl,al
        mov dx,INPUT_STATUS
waith1:
        in al,dx
        test al,1
        jnz waith1
waith2:
        in al,dx
        test al,1
        jz waith2
        dec cl
        cmp cl,0
        jnz waith1
        ret

TxtErr1 DB "shoes.bmp not found!",7,10,13,"$"

Boot loader with image 320×200 256 colors.

This is a work in progress, below are my Lab notes.

I want to rewrite pieces we made for a demo, loading images and effects from a floppydisk bootloader.

Without looking at old code (which was written using Masm), I wanted to learn the steps using Fasm.

I started with a boot sector program, It should do the following.

  • Set graphic mode, and start a trackloader
  • Load sector 2 and 3 which contains the color palette for the image.
  • Next sectors, cylinders and heads contain the raw image

I got it working, half that is.
In the past I used real disks, and now a virtual disk, maybe thats the difference?

First Code

use16
org 0x7c00

mov ah,0x0
mov al,0x13
int 10h

mov ax, 0a000h
mov es, ax
xor di, di
mov ax, 50
mov cx, 64000
rep stosb

loophere:
	jmp loophere


times 510 - ($-$$) db 0

dw 0xaa55

Compiling and starting:

fasm mybootblock.asm
qemu-system-x86_64 --drive format=raw,file=mybootblock.bin

This works, it sets the graphical mode and clears the screen.

Second Code
Skipping the int 25h version

## Track read part
    xor ax, ax    ; DS = 0
    mov ds, ax
    cld
    mov ah, 2h    ; int13h function 2 track read
    mov al, 2     ; number of tracks ( should be 2 for reading only palette)
    mov ch, 0     ; from cylinder number 0
    mov cl, 2     ; the sector number 2 - second sector (starts from 1, not 0)
    mov dh, 0     ; head number 0
    xor bx, bx    ; BX = 0
    mov es, bx    ; ES = 0
    mov bx, 7e00h ; Offset from above
    int 13h

    call setpal

## End part with setpalette routine
## appending palette.colors
## and a raw image

setpal:	
    mov dx,3c8h
    xor al,al
    mov di, ax
    out dx,al
    inc dx
    mov cx,256*3
    mov si,07e00h
    rep outsb
    ret
times 510 - ($-$$) db 0

dw 0xaa55
include 'palette.colors'

times 2048 - ($-$$) db 0
file 'image.raw'

Seems there is still a header on the RAW file, lets look at how I made this.

NOTE! .. Below converts an image with a STANDARD VGA palette, not a custom one as used above

Looking with ghex at the file I saw that there was a header 0x415 bytes large.
(Probably still palette colors in there)

dd if=shoes.bmp of=cutshoe.bmp bs=1 skip=1078 (0x415h + 3?)
worked for me

Loading the extra tracks didn’t work for me?!?!
But how could I define tracks/sectors and heads on a virtual floppy?

I tried to write sectors using debug.com

start dosbox
imgmount a: /tmp/floppy.img -t floppy
debug.com bootsector.bin
-r bx 01 

-r cx 512
; set bx:cx for size
-w 100 0 0 1
; write from address 100, drive 0 (a), sector 0, number of sectors

; testing
-l 100 0 0 1
;load sector to addr 100 drive 0 sector 0 number of sectors 

This used to work with real disks on a real machine, not in dosbox ?!?!

my way to create a disk in linux

dd if=bootblock.bin of=disk1.img bs=512 count=1 seek=0
dd if=palette.col of=disk1.img bs=512 count=1 seek=1 # or 2?
dd if=shoes.raw of=disk1.img bs=512 count=10000 seek=17

It looks like I can’t read futher than 18 sectors on a virtual floppy.
What next? Head=1? Cylinder=1?
Below the info from a floppy image before altering.

DOS/MBR boot sector, code offset 0x3c+2, OEM-ID “MSDOS5.0”, root entries 224, sectors 2880 (volumes <=32 MB), sectors/FAT 9, sectors/track 18, serial number 0x1c2a0d0f, unlabeled, FAT (12 bit), followed by FAT

Appending the RAW to a executable gave me problems to.
(Without making a bootdisk)

Above and below weird data. Appending the data to the executable needs some work also.

At least for today let me display this image correctly 🙂

use16
org 0x100

; set mode 320x200 256 colors palette
	mov ah,0x0
	mov al,13h
	int 10h

; clear screen routine, not really needed
clearscreen:
	push ax
	mov ax, 0a000h
	mov es, ax
	pop ax
	xor di, di
	inc ax
	mov cx, 64000 ; 320x200
	rep stosb

; call file loader 
	call Loadfile

; after displaying the image or displaying an error, wait for keypress to exit
waitforkeyloop:
	MOV AH,1
	INT 16h
	JZ waitforkeyloop
	XOR AH,AH
	INT 16h
Exit:
	MOV AX,3	; default text mode 3
	INT 10h
	MOV AX,4C00h	; exit to dos (terminate process)
	INT 21h

Loadfile:
	MOV DX,filename
	MOV AX,3D00h	; open filehandle
	INT 21h
	JC Err1
	MOV BX,AX   	; filehandle
	MOV CX,0FFFFh 	; size
	mov dx,0a000h 	; destination 0000:a000h - Screen memory
	mov ds,dx

	MOV DX,0
	MOV AH,3Fh	; read from file
	INT 21h
	JC  Err1
	MOV AH,3Eh	; close filehandle
	INT 21h

	RET
; print error
Err1:
	push cs		; make ds same as cs
	pop ds
	MOV DX,TxtErr1	; error
	MOV AH,09h
	INT 21h
	RET

filename DB "shoes.bmp",0
TxtErr1 DB "shoes.bmp not found!",7,10,13,"$"

Tomorrow .. back to the track loader

Mikrotik Wifi, 80386 and Lilygo streaming

Quiet days, I working on some art.

But here are the last ‘prutsen’

My current Wifi setup

I’ve got a Wifi outside of my network for guest and emergency. ( 2 SSIDs)

Then a main Wifi router in my livingroom, one in my workshop/studio and one in the Attic (Electronics Lab)

So three main Wifi AccessPoints. These all have the same SSID’s but on different frequencies. That way i’ve got roaming in and outside my house.
Also some virtual accesspoints are configured.
I’ve got a main, folkband, IOT, guest-inside all on 2.4Ghz and 5Ghz.

I watched a lot of YT presentations about Mikrotik Wifi.

So I ended up with DFS safe channels 20Mhz for 2.4 and 20/40Mhz Ce for 5Ghz. (subchannels for each after some frequency scanning)
(2.4 does a failback to 20Mhz whenever there is even one client detected which connects only on this band. Such as some old IOT stuff)
2.4 in only 1,6 and 11 no overlap, each on another device.
300Mbps is sufficient for my wifi 🙂

I’ve got accesslists in place and i’m going to read into kicking a client when the signal strenght is below -50dB

80386 (DX) Computer

Besides my 8088 and 8086 machines I needed a machine which could run our old demo’s. So I bought a new toy.

It has 8Mb Ram and runs at 40Mhz.

I’ve noticed that many of my VGA register manipulation code, can’t be run on a modern VGA monitor, I need to use a CRT for that .. Another thing to buy

Lilygo T-Display S3 Streaming

Not my code: https://github.com/Steve5451/esp32-stream-desktop
A very cool project!

Needed to fix arduino code, due to the TFT_eSPI library issues.
And I’ve got a S3 with another resolution, but that was an easy fix.
Then needed to reinstall nodejs with another version.
Had to modify the code because the tcp server would not start.
Weird errors logging, but in the end fixed … very cool

I probably end up designing a 3D printed case that looks like a monitor or tv.

Triple screen panorama viewer

Got a question, could I make the video viewer also for images.

Well, that is a great idea, i’ve got some panoramic photos myself.

A little modification, some added code, but here is a working example.

Some vacation pictures widescreen …

CODE
imageview.py filename
Use esc to stop, and enter for next image.
(Has better full screen experience than my movie player. (no padding) have to revisit that one )

Nice to have?

  • Back button?
  • Comments, renaming thumb
from pathlib import Path
from sys import platform as PLATFORM
import os
import re
import PySimpleGUI as sg
from PIL import Image, ImageEnhance, ImageTk, ImageOps, ImageFilter
from xml.etree import ElementTree as ET
import sys
from sys import platform as PLATFORM

abspath = os.path.abspath(__file__)
dname = os.path.dirname(abspath)
os.chdir(dname)

try:
    image=sys.argv[1]
except:
    print(sys.argv[0] +  " filename")
    exit()


def nextFile(currentfile,dir):
        newfile=""
        dirpath = os.path.dirname(dir)
        fileList = []
        for f in os.listdir(dirpath):
            #fpath = os.path.join(dirpath, f)
            fpath = f
            if os.path.isfile(fpath) and f.endswith(('.jpg','.JPG')):
                fileList.append(fpath)
        fileList.sort()
        for i in range(len(fileList)):
            try:
                if (fileList[i]) == currentfile:
                    newfile=fileList[i+1]
                    break
            except:
                newfile=fileList[0]
        return newfile
# yeah i know .. no thumb but full image, change it yourself!
def loadthumb(thumbfile):
    # IF exists
    path_to_file = thumbfile
    path = Path(path_to_file)

    if path.is_file():
        im = Image.open(thumbfile)
        im=ImageOps.contain(im, (5760,5760)) 
        thumbimage = ImageTk.PhotoImage(image=im)

        window['image'].update(data=thumbimage)
    else:
        window['image'].update("")

sg.theme('SystemDefaultForReal')
#------- Layout image only --------#
layout = [
        [[sg.Image('', size=(5760, 1080), key='image',background_color='black',pad=(0, 0))],
          ]]

#------- Set window --------#
window = sg.Window('Triple image player', layout, no_titlebar=True, margins=(0,0),location=(0,0), size=(5760,1080), keep_on_top=True, finalize=True,resizable=False)

window.bring_to_front()
window.Maximize()
window.bind("<Escape>", "-ESCAPE-")
window.bind("<Return>", "-ENTER-")

window['image'].expand(True, True)               

loadthumb(image)
nextfile = image
#------------ The Event Loop ------------#
while True:
    event, values = window.read(timeout=1000)       # run with a timeout so that current location can be updated
    if event == sg.WIN_CLOSED:
        break

    if event == '-ENTER-':
        nextfile = nextFile(nextfile,'./')
        loadthumb(nextfile)

    if event == '-ESCAPE-':
        window.close()
window.close()

Converting images for right resolution from a temp directory filled with large panorama photos

ls temp  | while read; do
	convert -resize 5760x -gravity center  -crop 5760x1080 -auto-orient  "temp/$REPLY" "$REPLY" 
done

Triple screen movie player in python

I didn’t find an easy and working movie player for wide screen setups.
Like double/triple monitor setups.

I’ve got 3x 1920×1080 monitors connected to my battlestation.
With a resolution of 5760×1080

Simple Python code to play a movie fullscreen

# importing vlc module
import vlc
 
# creating vlc media player object
media_player = vlc.MediaPlayer()
 
# media object
media = vlc.Media("movie.mp4")
 
# setting media to the media player
media_player.set_media(media)
media_player.toggle_fullscreen()
 
# start playing video
media_player.play()
 

But trying to get this stable working, I resorted to pysimplegui

#!/usr/bin/env python3

import PySimpleGUI as sg
import vlc
import sys
from sys import platform as PLATFORM

try:
    movie=sys.argv[1] 
except:
    print(sys.argv[0] +  " filename")
    exit()

sg.theme('DarkBlue')

layout = [[sg.Image('', size=(5760, 1080), key='-VID_OUT-')]]
window = sg.Window('Triple movie player', layout, no_titlebar=True, margins=(0,0),location=(0,0), size=(5760,1080), keep_on_top=True, finalize=True,resizable=False)

window.bring_to_front()
window.Maximize()
window.bind("<Escape>", "-ESCAPE-")
window.bind("<Return>", "-ENTER-")

window['-VID_OUT-'].expand(True, True)

inst = vlc.Instance()
list_player = inst.media_list_player_new()

media_list = inst.media_list_new([])
list_player.set_media_list(media_list)
player = list_player.get_media_player()

if PLATFORM.startswith('linux'):
    player.set_xwindow(window['-VID_OUT-'].Widget.winfo_id())
else:
    player.set_hwnd(window['-VID_OUT-'].Widget.winfo_id())

media_list.add_media(movie)
list_player.set_media_list(media_list)
list_player.play()
while True:
    event, values = window.read(timeout=1000)

    if event == sg.WIN_CLOSED:
        break
    if event == '-ENTER-':
        list_player.play()
    if event == '-ESCAPE-':
        list_player.stop()
        window.close()
window.close()

I’ve converted some of my Vuze media to the correct resolution using kdenlive.

I’ve added a new profile. 5760×1080 dont forget to change the display ratio!

Kdenlive howto

NFO creator with poster art generator

Created this for usage with Kodi, for our own personal movies, which can’t be scraped by movie scrapers obviously.
(dvd-rip,digital video, mobile movies, OBS, Vuze and Nikon movies for example)

UPDATE V2 added functions

This is a followup on:

Works on Windows also!

Needed libraries: (install with pip)
pathlib, Pillow, pymediainfo,PySimpleGUI,python-vlc

NFO created

Title – editable (generated from filename)
Duration – mediainfo data in seconds
Plot – Single line type it yourself
Actors – Predefined
Tags – Predefined checkboxes
Country – Pulldown ( I use this as location, Scotland, Asia, Garden)
Year – default 2023, copy button from mobile phone metadata year
Genre – Pulldown

Works

  • File select and loaded
  • Play movie, scrub thretro movie
  • Create snapshot
  • Play/Mute/Pause
  • Playtimer
  • Auto update NFO text field with selectable options
  • Poster art – rotate
  • Poster art – brightness
  • Reload Thumb after snapshotting
  • Year extracted from filename
  • add tag field
  • Next file (button AND functionality)
  • checkboxes from text list
  • Read NFO from file into fields
  • Poster art – aspect ratio

Needs work

Wish list

  • Poster art – contrast?
  • Low – more mediainfo?
  • Low – Media rotate??

The GUI in action (V1)

Kodi example

CODE ( WIP ) V2
I place this python script in every library directory. So i can change the checkboxes and the NFO being generated.
I fill the directory with symlinks for every movie I want to have included in this directory, but this is not needed. (thats the way I like to do things)
(This is the directory I scrape into Kodi)

from pathlib import Path
from sys import platform as PLATFORM
import os
import re
import PySimpleGUI as sg
import vlc
from pymediainfo import MediaInfo
from PIL import Image, ImageEnhance, ImageTk, ImageOps, ImageFilter
from xml.etree import ElementTree as ET


'''
WARNING, NFO overwrites previous!!



'''

alltags = ["music", "pipes", "computer","bbq","fun","art","travel","hobby","family","retro","compilation","retro","vuze"]

#------- WINDOWS FIX --------#
if PLATFORM.startswith('linux'):
    print("yeah linux baby!")
else:
    os.add_dll_directory(r'E:\VLC')

duration=0
tmptag=""

def nextFile(currentfile,dir):
        newfile=""
        dirpath = os.path.dirname(dir)
        fileList = []
        for f in os.listdir(dirpath):
            #fpath = os.path.join(dirpath, f)
            fpath = f
            if os.path.isfile(fpath) and f.endswith(('.mp4', '.mov', '.mpg','.avi','.mkv','.3gp')):
                fileList.append(fpath)
        fileList.sort()
        for i in range(len(fileList)):
#            print("fileList[i] " + fileList[i])
            try:
                if (fileList[i]) == currentfile:
                    newfile=fileList[i+1]
                    break
            except:
                newfile=fileList
        return newfile

def loadthumb(thumbfile):
    # IF exists
    path_to_file = thumbfile
    path = Path(path_to_file)

    if path.is_file():
        im = Image.open(thumbfile)
        im=ImageOps.contain(im, (640,640)) 
        thumbimage = ImageTk.PhotoImage(image=im)
        window['thumb'].update(data=thumbimage)
    else:
        window['thumb'].update("")

def loadnfo(file):

    #LOAD NFO
    nfo=file + ".nfo"
    path = Path(nfo)

    if path.is_file():
            f = open(nfo, "r")
            innfo=f.read()
            f.close()
#            print ("file : " + nfo)
            innfo="".join([s for s in innfo.strip().splitlines(True) if s.strip()])

# Clear some fields
            window['year'].update("")
            window['plot'].update("")

            # Update fields test
            if ET.fromstring(innfo).find('country') is not None:
                nfolocation = ET.fromstring(innfo).find('country')
                window['location'].update(value=nfolocation.text)
            if ET.fromstring(innfo).find('year') is not None:
                nfoyear = ET.fromstring(innfo).find('year')
                window['year'].update(nfoyear.text)
            if ET.fromstring(innfo).find('plot') is not None:
                nfoplot = ET.fromstring(innfo).find('plot')
                window['plot'].update(nfoplot.text)
            if ET.fromstring(innfo).find('title') is not None:
                nfotitle = ET.fromstring(innfo).find('title')
                window['-NM-'].update(nfotitle.text)
            if ET.fromstring(innfo).find('genre') is not None:
                nfogenre = ET.fromstring(innfo).find('genre')
                window['genre'].update(value=nfogenre.text)

            window['coline'].update(False)
            window['henri'].update(False)
            window['monique'].update(False)
            for actor in ET.fromstring(innfo).findall('actor'):
                name = actor.find('role').text
                if name == "Coline":
                    window['coline'].update(True)
                if name == "Henri":
                    window['henri'].update(True)
                if name == "Monique":
                    window['monique'].update(True)
            for alltag in alltags:
                window[alltag].update(False)

            if ET.fromstring(innfo).find('tag') is not None:
                for tag in ET.fromstring(innfo).findall('tag'):
                    if tag.text in window.AllKeysDict:
                        window[tag.text].update(True)

            window['coline'].update(False)
            window['henri'].update(False)
            window['monique'].update(False)
            for actor in ET.fromstring(innfo).findall('actor'):
                name = actor.find('role').text
                if name == "Coline":
                    window['coline'].update(True)
                if name == "Henri":
                    window['henri'].update(True)
                if name == "Monique":
                    window['monique'].update(True)
            for alltag in alltags:
                window[alltag].update(False)

            if ET.fromstring(innfo).find('tag') is not None:
                for tag in ET.fromstring(innfo).find('tag'):
                    window[tag.text].update(True)

    else:
        innfo=""
    window['LOADNFO'].update(innfo)





#------- Button definition --------#
def btn(name):  
    return sg.Button(name, size=(6, 2), pad=(1, 1))

#def tag(name):  
#    return sg.Checkbox(name, enable_events=True)

#------- GUI definition & setup --------#
sg.theme('SystemDefaultForReal')


l1=sg.Text("New title")
l2=sg.Multiline(" ", expand_x=True, key='-OUT-', expand_y=True,justification='left', size=(20,15))
l3=sg.Multiline(" ", expand_x=True, key='LOADNFO', expand_y=True,justification='left', size=(20,15))
l4=sg.Text("Plot")
t1=sg.Input("", key='-NM-')
t2=sg.Input("", key='plot',enable_events=True)
cb=[]

for alltag in alltags:
    cb.append(sg.Checkbox(alltag, key=alltag, enable_events=True))




cb.append(sg.Input("", key='addtag',size=(5, 1), enable_events=True))
cb.append(sg.Combo(["Lab","Datacenter","home","Outside","Outside","Lloydwebber","Steenweg","Enschede","Netherlands","Germany","Italy","Scotland","Canada","Egypt","Belgium","Sweden","Ireland","Asia","NewZealand","Hilversum"],default_value='Lab',key='location'))
cb.append(sg.Combo(["Storage","Funny","Vacation","Music","Relation"],default_value='Storage',key='genre'))
nameyear=[]
nameyear.append(sg.Checkbox("Coline", key='coline', enable_events=True,default=True))
nameyear.append(sg.Checkbox("Henri", key='henri', enable_events=True,default=True))
nameyear.append(sg.Checkbox("Monique", key='monique', enable_events=True,default=False))
nameyear.append(sg.Input("2023",key='year',size=(5, 1), enable_events=True))
nameyear.append(sg.Button("copyyear\nmetadata", key='copymeta', size=(20, 1), pad=(1, 1), enable_events=True))
nameyear.append(sg.Input("",key='metayear',size=(5, 1), enable_events=True))
nameyear.append(sg.Button("copyyear\nfilename", key='copyname', size=(20, 1), pad=(1, 1), enable_events=True))
nameyear.append(sg.Input("",key='fileyear',size=(5, 1), enable_events=True))
b1=sg.Button("WRITE NFO", key='write', enable_events=True)
b2=sg.Button("Exit", key='exit', enable_events=True)

#------- Layout total --------#

layout = [[sg.Input(key='-VIDEO_LOCATION-', visible=False, enable_events=True),
          sg.FileBrowse(file_types=(("Video files", "*.mkv *.mov *.mp4 *3gp *avi *mpg"),)),btn('next')],
          [sg.Text('Load media to start', key='-MESSAGE_AREA-')],
          [sg.Image('', size=(600, 480), key='-VID_OUT-', pad=(1, 1)),sg.Image('', size=(600, 480), key='thumb', pad=(1, 1))],
          [btn('mute'), btn('play'), btn('pause'), btn('stop'), btn('snap'), btn('snaprot'), btn('lighten'), btn('darken'), sg.Combo(["4:3","16:9","9:16"],key='aspect',enable_events=True)],
          [sg.Slider(range=(0, 1000), default_value=1, expand_x=True, enable_events=True, orientation='horizontal', key='-SL-')],
          [sg.Text('org title', key='orgtitle')],
          [l1, t1, l4, t2],[cb],[nameyear],[b1, l2, l3, b2],
          [sg.Text('GENERATED                            -= mini nfo writer =-                              PREVIOUS', key='footer')]
          ]

#------- Set window --------#
window = sg.Window('Mini NFO generator', layout, element_justification='center', finalize=True, resizable=True)
#------- VID out window --------#
window['-VID_OUT-'].expand(True, True)               
#------------ Media Player Setup ---------#
inst = vlc.Instance()
list_player = inst.media_list_player_new()
media_list = inst.media_list_new([])
list_player.set_media_list(media_list)
player = list_player.get_media_player()
if PLATFORM.startswith('linux'):
    player.set_xwindow(window['-VID_OUT-'].Widget.winfo_id())
else:
    player.set_hwnd(window['-VID_OUT-'].Widget.winfo_id())

#------------ I Want default mute ---------#
player.audio_set_mute(True)

#------------ The Event Loop ------------#
while True:
    event, values = window.read(timeout=1000)       # run with a timeout so that current location can be updated
    if event == sg.WIN_CLOSED:
        break



    if event == 'mute':
        mutevalue = player.audio_get_mute()
        if mutevalue==0:
            player.audio_set_mute(True)
        if mutevalue==1:
            player.audio_set_mute(False)
    if event == 'play':
        list_player.play()
    if event == 'pause':
        list_player.pause()
    if event == 'stop':
        list_player.stop()
    if event == 'snap':
        list_player.pause()
        filename = values['-VIDEO_LOCATION-']
        shortfilename = filename.rsplit( ".", 1 )[ 0 ] 
        newname = shortfilename + "-poster.png"
        player.video_take_snapshot(0, newname, 0, 0)
        loadthumb(newname)
    if event == 'snaprot':
        list_player.pause()
        filename = values['-VIDEO_LOCATION-']
        shortfilename = filename.rsplit( ".", 1 )[ 0 ] 
        newname = shortfilename + "-poster.png"
        im1 = Image.open(newname)
        im1 = im1.rotate(90, Image.NEAREST, expand = 1)
        im1 = im1.save(newname)
        loadthumb(newname)

    if event == 'lighten':
        list_player.pause()
        filename = values['-VIDEO_LOCATION-']
        shortfilename = filename.rsplit( ".", 1 )[ 0 ] 
        newname = shortfilename + "-poster.png"
        im1 = Image.open(newname)
        im1 = ImageEnhance.Brightness(im1)
        im1 = im1.enhance(1.2)
        im1 = im1.save(newname)
        loadthumb(newname)
        
    if event == 'darken':
        list_player.pause()
        filename = values['-VIDEO_LOCATION-']
        shortfilename = filename.rsplit( ".", 1 )[ 0 ] 
        newname = shortfilename + "-poster.png"
        im1 = Image.open(newname)
        im1 = ImageEnhance.Brightness(im1)
        im1 = im1.enhance(0.8)
        im1 = im1.save(newname)
        loadthumb(newname)

    if event == 'aspect':
        filename = values['-VIDEO_LOCATION-']
        shortfilename = filename.rsplit( ".", 1 )[ 0 ] 
        newname = shortfilename + "-poster.png"
        if values['aspect'] == "4:3":
            im1 = Image.open(newname)
            im1 = im1.resize((1920 ,int(1920/4*3)), Image.ANTIALIAS)
            im1 = im1.save(newname)
            loadthumb(newname)
        if values['aspect'] == "16:9":
            im1 = Image.open(newname)
            im1 = im1.resize((1920 ,int(1920/16*9)), Image.ANTIALIAS)
            im1 = im1.save(newname)
            loadthumb(newname)
        if values['aspect'] == "9:16":
            im1 = Image.open(newname)
            im1 = im1.resize((int(1920/16*9),1080), Image.ANTIALIAS)
            im1 = im1.save(newname)
            loadthumb(newname)


    if event == 'write':
        filename = values['-VIDEO_LOCATION-']
        shortfilename = filename.rsplit( ".", 1 )[ 0 ] 
        newname = shortfilename + ".nfo"
        f = open(newname, "w")
        f.write(out)
        f.close()
        loadnfo(shortfilename)

    if event == 'copymeta':
        if datefromfile is not None:
            window['year'].update(datefromfile)

    if event == 'copyname':
        if fileyear is not None:
            window['year'].update(fileyear)


    if event == '-SL-':
        pos=int(values['-SL-'])
        player.set_position(pos/ 1000.0)

        
    if values['coline'] == False:
         colinetext = ""
    if values['coline'] == True:
        colinetext = """<actor>
  <name>Coline Lastname</name>
  <role>Coline</role>
</actor>"""
    if values['henri'] == False:
         henritext = ""
    if values['henri'] == True:
        henritext = """<actor>
  <name>Henri Aanstoot</name>
  <role>Henri</role>
</actor>"""
    if values['monique'] == False:
            moniquetext = ""
    if values['monique'] == True:
        moniquetext = """<actor>
  <name>Monique Lastname</name>
  <role>Monique</role>
</actor>"""

        
    if event == '-VIDEO_LOCATION-':
        if values['-VIDEO_LOCATION-'] and not 'Video URL' in values['-VIDEO_LOCATION-']:
            media_list.remove_index(0)
            media_list.add_media(values['-VIDEO_LOCATION-'])
            list_player.set_media_list(media_list)
            shortname = values['-VIDEO_LOCATION-']
            shortname = shortname.rsplit( "/", 1 )[ 1 ] 
            nextfile=nextFile(shortname,"./")
            shortname = shortname.rsplit( ".", 1 )[ 0 ] 
            window['-NM-'].update(shortname) # only add a legit submit
            list_player.stop()
            list_player.next()
            thumbname=shortname + "-poster.png"
            loadthumb(thumbname)

            #Print org title
            window['orgtitle'].update(values['-VIDEO_LOCATION-'])


            loadnfo(shortname)


            #DURATION
            mi = MediaInfo.parse(values['-VIDEO_LOCATION-'])
            duration=int(mi.tracks[0].duration/1000)
            
            #DATE FROM media
            metadate=mi.tracks[0].encoded_date
            if metadate is not None:
                datefromfile=next(iter(re.findall(r"[1][9][8-9][0-9]|[2][0][0-9]{2}", metadate)), None)
                window['metayear'].update(datefromfile)

            # YEAR FROM FILENAME 
            fileyear=next(iter(re.findall(r"[1][9][8-9][0-9]|[2][0][0-9]{2}", shortname)), None)
            # year from filename
            if fileyear is not None:
                window['fileyear'].update(fileyear)


    if event == 'next':
        
        values['-VIDEO_LOCATION-'] = nextfile
        window['-VIDEO_LOCATION-'].update(nextfile)

        if values['-VIDEO_LOCATION-'] and not 'Video URL' in values['-VIDEO_LOCATION-']:
            media_list.remove_index(0)
            media_list.add_media(values['-VIDEO_LOCATION-'])
            list_player.set_media_list(media_list)
            shortname = values['-VIDEO_LOCATION-']
 #           shortname = shortname.rsplit( "/", 1 )[ 1 ] 
            nextfile=nextFile(shortname,"./")
            #print(nextfile)
            shortname = shortname.rsplit( ".", 1 )[ 0 ] 
            window['-NM-'].update(shortname) # only add a legit submit
            list_player.stop()
            list_player.next()
            thumbname=shortname + "-poster.png"
            loadthumb(thumbname)

            #Print org title
            window['orgtitle'].update(values['-VIDEO_LOCATION-'])
            

            loadnfo(shortname)

            #DURATION
            mi = MediaInfo.parse(values['-VIDEO_LOCATION-'])
            duration=int(mi.tracks[0].duration/1000)
            
            #DATE FROM media
            metadate=mi.tracks[0].encoded_date
            if metadate is not None:
                datefromfile=next(iter(re.findall(r"[1][9][8-9][0-9]|[2][0][0-9]{2}", metadate)), None)
                window['metayear'].update(datefromfile)

            # YEAR FROM FILENAME 
            fileyear=next(iter(re.findall(r"[1][9][8-9][0-9]|[2][0][0-9]{2}", shortname)), None)
            # year from filename
            if fileyear is not None:
                window['fileyear'].update(fileyear)
#        media_list.add_media(nextfile)
##        shortname = nextfile.rsplit( ".", 1 )[ 0 ] 
##        window['-NM-'].update(shortname) # only add a legit submit
#        print(nextfile)
#        window['-VIDEO_LOCATION-'].update(nextfile)
#        list_player.next()
# hier moeten wat update dingen bij!




    # Add tag
    if event == 'addtag':
        tmptag="<tag>" + values['addtag'] + "</tag>"

#------------ exit ---------#

    if event=='exit':
         exit()
#------------ TAGS ---------#
    
    subs=[x.Text for x in cb if x.get()==True]
    for idx, x in enumerate(subs):
        subs[idx]="<tag>" + subs[idx] + "</tag>"


    if values['year'] == False:
        year=str(2023)
    else:
        year=values['year']
    if values['location'] == False:
        location="Lab"
    else:
        location=values['location']
    if values['plot'] == False:
        plot=""
    else:
        plot=values['plot']
    if values['genre'] == False:
        genre="Storage"
    else:
        genre=values['genre']
#------------ NFO TEXT FIELD ---------#

    out="""<movie>
<title>{}</title>
<plot>{}</plot>
<genre>{}</genre>
<duration>{}</duration>
<year>{}</year>
<country>{}</country>
{}
{}
{}
{}
{}
</movie>
""".format(values['-NM-'],plot,genre,duration,year,location,colinetext,henritext,moniquetext,tmptag, "\n".join(subs))
    out="".join([s for s in out.strip().splitlines(True) if s.strip()])
    window['-OUT-'].update(out)

#------------ Messages and timer ---------#

    # update elapsed time if there is a video loaded and the player is playing
    if player.is_playing():
        window['-MESSAGE_AREA-'].update("{:02d}:{:02d} / {:02d}:{:02d}".format(*divmod(player.get_time()//1000, 60),
                                                                     *divmod(player.get_length()//1000, 60)))
    else:
        window['-MESSAGE_AREA-'].update('Load media to start' if media_list.count() == 0 else 'Ready to play media' )

window.close()