The Cookie Machine - Click here to drag window

DUMMY TEXT - Real text set in assets/js/theCookieMachine.js

If you can read me, I'm broken!

ToC Skip

Introduction

mserve (Music Server) is firstly a Music Player that entertains with animated graphics, VU meters and scrolling lyrics. Secondly, it encodes CDs with track titles and artwork automatically obtained from MusicBrainz. Finally, mserve automatically downloads lyrics scores from the internet. Lyrics score lines are synchronized by simply clicking each line as it is sung.

Take a quick peek at one of the many merve videos on this website.

Features

mserve has regular features you would expect. This section lists some unique features you might not expect.

Innovative Features Most Applications Don’t Have:

Features Most Music Players Don’t Have:

Multiple Everything

mserve remembers and restores positions and sizes for multiple windows plus a whole lot more.

Under Construction

Under Construction.png
Under Construction.png

mserve is still under construction. Installation requires manually downloading files from GitHub and installing any missing dependencies with apt get install in Debian/Ubuntu or pip install on other Operating Systems.

See the required dependencies for mserve section for more details. For typical power users, many of the dependencies will already be installed.

IMPORTANT NOTES:

mserve Installation

mserve (Music Server) is written in Python. The main program is called mserve.py and can be found in the mserve GitHub Repository ⧉ 🔗.

Copy all files in the GitHub src folder to a new directory on your machine. For example, <HOME>/mserve for Linux, Mac, Chrome OS or Windows Subsystem for Linux (WSL). Unless you are running Ubuntu 16.04 LTS under Extended Security Maintenance (ESM), you will have to make changes to mserve.py and the programs it calls.

For Windows, the installation directory would be <HOME>\mserve As of September 15, 2024 mserve will not run under Windows without modification.

mserve.py is called with m from the command line or a desktop shortcut. It is recommended you start using mserve from the command line to see any error messages that might appear.

m is a wrapper Python script that centers a logo on your screen for a moment while mserve is loaded into memory. Using m instead of mserve.py speeds up loading because mserve.pyc is automatically called and it is half the size. As of August 30, 2023, the former is 765 KB and the latter is 409 KB.

m and mserve.py do not need to be added to your path. You can call them with /path/to/m or /path/to/mserve.py from the command line. If you followed the installation tip above, it would be <HOME>/mserve/m for Linux-like machines, or <HOME>\mserve\m for Windows. As of August 30, 2023, mserve will not run under Windows without modification.


Top ToS Skip

Table of Contents


Top ToS ToC Skip

Music Location Tree

The Music Location Tree is the main window which appears when mserve starts up and it remains until mserve is closed. On startup, all files in the Music Location Tree are rediscovered. New songs since the last time are displayed.

Songs are stored under collapsed Album Names which, in turn, are stored under collapsed Artist Names. The directory format must be:

Only Song Filenames with a music type extensions are included. For example, extensions of .flac, .mp3, .m4a, .oga, .wav, etc, are included.

Here is a sample window with currently playing song highlighted in green.

mserve.py Music Location Tree.png
mserve.py Music Location Tree.png

The first three Artists are “collapsed” which is indicated by the “▶” chevron (A.K.A. “right triangle). When you click the “▶” chevron the Artist is opened and the “▌” chevron is displayed to indicate the Artist is opened. The same chevrons and used for Albums. As songs play and end in mserve, the Album Name and Artist Name are automatically expanded and collapsed to show the green highlight bar.

TIP: Double-click on an Artist or Album to expand and collapse entries underneath.

Songs have a checkbox which are clicked to include or exclude in playlists. The check box is colored solid when “checked” and is hollow when “unchecked”. If a line appears, it indicates the Artist or Album is “tri-state”. This means some songs below are “checked” and some are “unchecked”.

You can check and uncheck individual songs, entire Artists or, entire Albums.

As you check and uncheck individual songs, or entire Artists or, entire Albums a list is built in memory. Then you can Apply or Cancel changes.

New songs are added into the Chronology (Playlist) after the current playing song position.

If you make huge mistakes you can abandon changes with the option “Exit without saving Playlist”.

The Music Location Tree window follows the directory structure of your storage device:

NOTE: “My Music” is an over simplification for the sake of example. You can start m (the splash screen for mserve.py) by typing: m "/mnt/music/Users/Person/Music/iTunes/iTunes Media/Music/"

You can also call m after changing to a music directory. E.G. Type: cd ~/Music/Pink Floyd and press Enter . Then type: m . and press Enter . Note the . specifies the current directory and m must be in your current path. mserve will open and display all the Albums for Pink Floyd.

If you don’t pass a parameter to m it will reload the last location used and continue playing favorites from where it left off.


The top-left corner of the Music Location Tree window contains four dropdown menus; File, Edit, View and Tools. Click on the name and the dropdown menu options appear:

File Dropdown Menu

Some options will be disabled out when they are not applicable. For example, the Save Playlist and Close Playlist options are disabled (greyed out) until a Playlist is opened.

Edit Dropdown Menu

View Dropdown Menu

NOTE: The three SQL views allow the column to be moved. Click and hold the heading to drag the column to a different position.

Tools Dropdown Menu

Sample videos for the Tools Dropdown Menu are available below with detailed explanations.

NOTE: The Enable TV Commercial Buttons and Enable FF/Rewind Buttons dropdown menu options occupy the same menu line and replace each other when they are clicked. The button selection only effects the the current Playlist. Other locations and playlists maintain their own button selection.


Right-Click Popup Menus

In all mserve windows, you can move the mouse over rows and they are highlighted. You can right click on a row for a context-sensitive popup menu.

When an Artist, Album or Song line in the Music Location Tree window is right-clicked, a context-sensitive popup menu appears. The context-sensitive popup menu changes depending on the line type, which is why it is called “context-sensitive”.

Artist or Album Right-Click Popup Menu

When you click on an Artist or Album, it is expanded and entries beneath are highlighted in yellow. The yellow highlighted entries is a reminder of what will be effected by the next action.

Song Right-Click Popup Menu

When you highlight and right click a song in the Music Location Tree, a context-sensitive popup menu appears.


Information Centre

Just like other applications, mserve uses dialog boxes to display:

However, unlike other applications, mserve also records these in the Information Centre for the duration of your session.

Additionally mserve records system events and user actions where there was no dialog box presented.

The Information Centre is accessed from the Music Location Tree window’s View dropdown menu. It can also be accessed by clicking the light blue thin ruler near the top of the window, as the video below illustrates.

Information Centre Sample Video


Music Location Tree Help Button

mserve windows contain “Help” buttons that:

Help Button Sample Video

Help Button Sample Video Highlights


Debug Information

Sample Debug Information

  ######################################################
 //////////////                            \\\\\\\\\\\\\\
<<<<<<<<<<<<<<    mserve - Music Server     >>>>>>>>>>>>>>
 \\\\\\\\\\\\\\                            //////////////
  ######################################################
                    Started: 2:15 PM
FUSE library version: 2.9.4
using FUSE kernel interface version 7.19

global_variables.py (g) - Machine Information
==========================================================================================

g.OS_PLATFORM     : Linux-4.14.216-0414216-generic-x86_64-with-Ubuntu-16.04-xenial
g.OS_NAME         : Linux
g.OS_VERSION      : 4.14.216-0414216-generic
g.OS_RELEASE      : #202101171339 SMP Sun Jan 17 13:56:04 UTC 2021
g.USER            : rick
g.USER_ID         : 1000
g.HOME            : /home/rick
g.USER_CONFIG_DIR : /home/rick/.config/mserve
g.USER_DATA_DIR   : /home/rick/.local/share/mserve
g.MSERVE_DIR      : /home/rick/.local/share/mserve/
g.PROGRAM_DIR     : /home/rick/python/
g.TEMP_DIR        : /run/user/1000/
g.MSERVE_VERSION  : 3.5.0
SQL PRAGMA Version: 3 

Python Version    : 2.7.12 (default, Nov 20 2023, 15:01:59)  [GCC 5.4.0 20160609]
Sqlite3 Version   : 3.11.0
TK Version        : 8.6
Pillow Version    : 3.1.2
PIL Version       : 1.1.7

PulseAudio Version: pulseaudio 8.0
ffmpeg Version    : 7.0.1-static https://johnvansickle.com/ffmpeg/ 
ffplay Version    : 2.8.17-0ubuntu0.1+esm7
ffprobe Version   : 7.0.1-static https://johnvansickle.com/ffmpeg/ 

xdotool Version   : xdotool version 3.20150503.1
wmctrl Version    : 1.07
pqiv Version      : 2.2
kid3 Version      : kid3 3.3.1
nautilus Version  : GNOME nautilus 3.14.3
nmap Version      : Nmap 7.01 ( https://nmap.org )
SSH Version       : OpenSSH_7.2p2 Ubuntu-4ubuntu2.10+esm5, OpenSSL 1.0.2g  1 Mar 2016
sshfs Version     : SSHFS version 2.5
fusermount Version: fusermount version: 2.9.4
wakeonlan Version : /usr/bin/wakeonlan version 0.41 calling Getopt::Std::getopts ...

Distributor ID:	Ubuntu
Description:	Ubuntu 16.04.7 LTS
Release:	16.04
Codename:	xenial
xrandr program version       1.5.1
Server reports RandR version 1.5

mon = monitor.Monitors()
==========================================================================================

mon.screen_width x mon.screen_height: 5790 x 3240 

Number of monitors - mon.monitor_count: 3
for m in mon.monitors_list: -- self.debug_detail(' ', m):
  Monitor(number=0, name='HDMI-0', x=0, y=0, width=1920, height=1080, primary=False)
  Monitor(number=1, name='DP-1-1', x=1920, y=0, width=3840, height=2160, primary=False)
  Monitor(number=2, name='eDP-1-1', x=3870, y=2160, width=1920, height=1080, primary=True)

Primary Monitor - mon.primary_monitor:
   Monitor(number=2, name='eDP-1-1', x=3870, y=2160, width=1920, height=1080, primary=True)

Active Window Tuple -  active_win = mon.get_active_window():
  active_win.number   : 75497552
  active_win. WxH+X+Y : 1461 x 853 + 2755 x 60
  active_win.name     : SD Card SanDisk 128GBâ€ƒâ€ƒâ€ƒđŸŽ” 3,919 songs. 1,441 selected.â€ƒâ€ƒâ€ƒïżœ

Active Monitor - mon.get_active_monitor():
   Monitor(number=1, name='DP-1-1', x=1920, y=0, width=3840, height=2160, primary=False)

sys.getfilesystemencoding() UTF-8

Open Windows (Wnck) - mon.get_all_windows():
==========================================================================================

Window(number=46137346L, name='conky (alien)', x=5350, y=24, width=410, height=1392)
Window(number=65011714L, name='XdndCollectionWindowImp', x=-5890, y=-3340, width=5790, height=3240)
Window(number=65011721L, name='unity-launcher', x=-165, y=-1156, width=65, height=1056)
Window(number=65011742L, name='unity-panel', x=0, y=0, width=1920, height=24)
Window(number=65011749L, name='unity-panel', x=1920, y=0, width=3840, height=24)
Window(number=65011756L, name='unity-panel', x=3870, y=2160, width=1920, height=24)
Window(number=65011763L, name='unity-dash', x=-1343, y=-780, width=1243, height=680)
Window(number=65011764L, name='Hud', x=-420, y=-300, width=320, height=200)
Window(number=62914570L, name='Desktop', x=0, y=0, width=5790, height=3240)
Window(number=62918548L, name='L004', x=3870, y=2195, width=1369, height=628)
Window(number=81789286L, name='mserve', x=2257, y=363, width=1300, height=902)
Window(number=83886131L, name='Multiple Monitors Manager - mmm', x=2059, y=240, width=1447, height=838)
Window(number=10485837L, name='mserve \xe2\x80\x93 ~/python/mserve.py', x=2718, y=622, width=1518, height=1523)
Window(number=81792024L, name='Python1', x=2259, y=1269, width=1300, height=874)
Window(number=81792281L, name='Python3', x=2061, y=42, width=3274, height=366)
Window(number=10490282L, name='website \xe2\x80\x93 mserve.md', x=3938, y=823, width=1377, height=1327)
Window(number=100663307L, name='Mozilla Firefox', x=4015, y=2253, width=1775, height=987)
Window(number=100663338L, name='Kennedy teams up with Trump. Elensky talks down to Modi. Donbass collapsing, Ukraine stuck in Kursk - YouTube \xe2\x80\x94 Mozilla Firefox', x=0, y=0, width=1920, height=1080)
Window(number=63181825L, name='rick Properties', x=5266, y=2372, width=516, height=476)
Window(number=75497552L, name='SD Card SanDisk 128GB\xe2\x80\x83\xe2\x80\x83\xe2\x80\x83\xf0\x9f\x8e\xb5 3,919 songs.\xe2\x80\x831,441 selected.\xe2\x80\x83\xe2\x80\x83\xe2\x80\x83\xf0\x9f\x96\xb8 33.3 GB used.\xe2\x80\x8312.8 GB selected.\xe2\x80\x83\xe2\x80\x83\xe2\x80\x83\xe2\x98\xb0 L004 - Default Favorites - mserve', x=2755, y=60, width=1461, height=853)
Window(number=75501105L, name='Playing Favorites - mserve', x=3605, y=358, width=1704, height=865)

Saved Geometry for Windows - sql.Config.print_windows():
==========================================================================================

'Action':calculator	Window Name:Big Number Calculator Window
	'SourceMaster':	460x248+5273+2206
	'SourceDetail':	saved on exit, loaded on starting
	'Comments':	Used in conjunction with 'screen' History Record Id #
'Action':encoding	Window Name:CD Encoding (Ripping) Window
	'SourceMaster':	1713x738+2257+278
	'SourceDetail':	saved on exit, loaded on starting
	'Comments':	Used in conjunction with 'screen' History Record Id #
'Action':history	Window Name:Lyrics Scraping History Window (Future Use)
	'SourceMaster':	2128x697+2060+1187
	'SourceDetail':	saved on exit, loaded on starting
	'Comments':	Used in conjunction with 'screen' History Record Id #
'Action':lcs_top	Window Name:DELETE this SQL record
	'SourceMaster':	1268x730+3354+189
	'SourceDetail':	saved on exit, loaded on starting
	'Comments':	Used in conjunction with 'screen' History Record Id #
'Action':library	Window Name:Music Library Window (Main mserve Window)
	'SourceMaster':	1461x785+2755+60
	'SourceDetail':	saved on exit, loaded on starting
	'Comments':	Used in conjunction with 'screen' History Record Id #
'Action':location	Window Name:DELETE this SQL record
	'SourceMaster':	1113x756+3087+182
	'SourceDetail':	saved on exit, loaded on starting
	'Comments':	Used in conjunction with 'screen' History Record Id #
'Action':locations	Window Name:Locations Maintenance Window
	'SourceMaster':	1239x768+4005+77
	'SourceDetail':	saved on exit, loaded on starting
	'Comments':	Used in conjunction with 'screen' History Record Id #
'Action':playlist	Window Name:Music Playing Window
	'SourceMaster':	1704x837+3605+358
	'SourceDetail':	saved on exit, loaded on starting
	'Comments':	Used in conjunction with 'screen' History Record Id #
'Action':playlists	Window Name:Playlists Maintenance Window
	'SourceMaster':	1413x658+2908+199
	'SourceDetail':	saved on exit, loaded on starting
	'Comments':	Used in conjunction with 'screen' History Record Id #
'Action':pls_top	Window Name:DELETE this SQL record
	'SourceMaster':	1183x493+3144+53
	'SourceDetail':	saved on exit, loaded on starting
	'Comments':	Used in conjunction with 'screen' History Record Id #
'Action':results	Window Name:DELETE this SQL record
	'SourceMaster':	2086x720+2180+1246
	'SourceDetail':	saved on exit, loaded on starting
	'Comments':	Used in conjunction with 'screen' History Record Id #
'Action':sql_history	Window Name:View SQL History Table Window
	'SourceMaster':	1868x866+3050+217
	'SourceDetail':	saved on exit, loaded on starting
	'Comments':	Used in conjunction with 'screen' History Record Id #
'Action':sql_location	Window Name:View SQL Locations Table Window
	'SourceMaster':	1920x847+3298+337
	'SourceDetail':	saved on exit, loaded on starting
	'Comments':	Used in conjunction with 'screen' History Record Id #
'Action':sql_music	Window Name:View SQL Music Table Window
	'SourceMaster':	1871x830+2815+143
	'SourceDetail':	saved on exit, loaded on starting
	'Comments':	Used in conjunction with 'screen' History Record Id #

TOOLTIPS - tt.line_dump()
==========================================================================================

Tooltips Line Dump - 26 Tip Dictionaries
Tip#  Suf.  Name - Text
====  ====  ==============================================================================
#  1  9040  button  -  â–Œ â–Č â–Œ â–Č  Expanding/Collapsing Information Centre  â–Č â–Œ â–Č â–Œ 
            .140097342574176.140097341968464.140097341968824.140097341969040
#  2  3120  Cancel  -  Cancel changes. Playlist remains unchanged.
            .140097342574176.140097341968464.140097340302256.140097340303120
#  3  2800  Apply  -  Temporarily update changes to playlist in memory.
            .140097342574176.140097341968464.140097340302256.140097340332800
#  4  6448  ▶  Play  -  Lift music queue window up.
            .140097342574176.140097341968464.140097340334024.140097340346448
#  5  7744  🗘 Refresh library  -  Scan disk for songs added and removed.
            .140097342574176.140097341968464.140097340334024.140097340347744
#  6  8968  🖾  Rip CD  -  Encode songs from Audio CD to music files.
            .140097342574176.140097341968464.140097340334024.140097340348968
#  7  0120  ⧉ Help  -  Open new window in default web browser for
            .140097342574176.140097341968464.140097340334024.140097340350120
#  8  0264  ✘ Close  -  Close mserve and any windows mserve opened.
            .140097342574176.140097341968464.140097340334024.140097340470264
#  9  9112  splash  -  Playlist: L004 - Default Favorites
            .140097342574176.140097341968464.140097341969904.140097341969112
# 10  7784  label  -  Speaker with diagonal line.
            .140097342017192.140097342474360.140097338815480.140097338817784
# 11  5192  label  -  Speaker with three waves.
            .140097342017192.140097342474360.140097338815480.140097338815192
# 12  5696  label  -  Volume slider active when music plays:
            .140097342017192.140097342474360.140097338815480.140097338815696
# 13  9096  canvas_button  -  Auto Scrolling lyrics is active.
            .140097342017192.140097342474360.140097339859528.140097339859672.140097339859096
# 14  9600  canvas_button  -  Lyrics line is highlighted using time index.
            .140097342017192.140097342474360.140097339859528.140097339859672.140097339859600
# 15  4488  canvas_button  -  Manual lyrics score scrolling is active.
            .140097342017192.140097342474360.140097339859528.140097339859672.140097339864488
# 16  5712  canvas_button  -  Manual lyrics score scrolling is active.
            .140097342017192.140097342474360.140097339859528.140097339859672.140097339865712
# 17  5352  label  -  x % time:
            .140097342017192.140097342474360.140097339859528.140097339859672.140097339865352
# 18  8656  canvas_button  -  Left-clicking hamburger icon displays a 
            .140097342017192.140097342474360.140097339859528.140097339859672.140097339868656
# 19  1176  ✘ Close  -  Close playlist but mserve stays open.
            .140097342017192.140097339868944.140097339871176
# 20  2496   🔀 Shuffle  -  Shuffle songs randomly.
            .140097342017192.140097339868944.140097340472496
# 21  5136  ⏼  Previous  -  Play previous song.
            .140097342017192.140097339868944.140097340305136
# 22  8128  âȘ  -10 sec  -  Rewind song 10 seconds back.
            .140097342017192.140097339868944.140097340388128
# 23  9208  ❚❚ Pause  -  Play music and spin artwork.
            .140097342017192.140097339868944.140097340389208
# 24  1152  +10 sec  ⏩  -  Fast Forward song 10 seconds ahead.
            .140097342017192.140097339868944.140097340391152
# 25  1088  Next  ⏭  -  Play next song in playlist.
            .140097342017192.140097339868944.140097340441088
# 26  4824  🖾 Hide Chronology  -  Hide the chronology playlist below
            .140097342017192.140097339868944.140097339884824

Opened Location
==========================================================================================

lcs.open_code       : L004
lcs.open_name       : SD Card SanDisk 128GB
lcs.open_modify_time: 1693330248.0
lcs.open_image_path : Sandisk 128GB.png
lcs.open_mount_point: MountPoint
lcs.open_topdir     : /media/rick/SANDISK128/Music
lcs.open_host       : 
lcs.open_wakecmd    : 
lcs.open_testcmd    : 
lcs.open_testrep    : 0
lcs.open_mountcmd   : 
lcs.open_touchcmd   : 
lcs.open_touchmin   : 0
lcs.open_comments   : Works in phone or laptop with Standard Adapter
lcs.open_row_id     : 4

Opened Playlist
==========================================================================================

No Playlist opened

Information Centre - self.info.dict[]
==========================================================================================

--- KEY ---   --- VALUE ---------------------

[time]      : 1724531842.38
[source]    : list[VALUES] on lines below.
  File "./m", line 86, in <module>
    main()

  File "./m", line 80, in main
    mserve.main(toplevel=splash, cwd=cwd, parameters=sys.argv)

  File "/home/rick/python/mserve.py", line 23581, in main
    MusicLocationTree(toplevel, SORTED_LIST)  # Build treeview of songs

  File "/home/rick/python/mserve.py", line 1673, in __init__
    self.load_last_selections()  # Play songs in favorites or playlists

  File "/home/rick/python/mserve.py", line 8175, in load_last_selections
    self.play_selected_list()

  File "/home/rick/python/mserve.py", line 8714, in play_selected_list
    if not self.play_one_song(resume=resume, chron_state=chron_state):

  File "/home/rick/python/mserve.py", line 10174, in play_one_song
    if not self.play_to_end():  # Play entire song unless next/prev, etc.

  File "/home/rick/python/mserve.py", line 10360, in play_to_end
    self.refresh_play_top()  # Rotate art, update vu meter after(.033)

  File "/home/rick/python/mserve.py", line 10449, in refresh_play_top
    self.play_top.update()  # Sept 20 2020 - Need for lib_top too

  File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1022, in update
    self.tk.call('update')

  File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1540, in __call__
    return self.func(*args)

  File "/home/rick/python/mserve.py", line 5999, in show_debug
    self.debug_output()

  File "/home/rick/python/mserve.py", line 6436, in debug_output
    collapsed=True, ms_font="TkFixedFont")

[type]      : fact
[severity]  : info
[action]    : open
[text]      : 
Opened Playlist
==========================================================================================

No Playlist opened

[text_start]: 
[text_end]  : 
[patterns]  : []
[collapsed] : True
[font]      : TkFixedFont
[view_time] : 1724531842.38

CURRENT SONG and COMMON VARIABLES
==========================================================================================

self.ndx   : 52  | Song iid: 1303  | 1-09 Dirty White Boy.m4a
tree values: [u'2:15:36 PM - 21 Minutes ago', 8, u'\u2116 \u2007\u200753', u'1724530536.0', 7601167, 1, 220, 7601167, 1, 220]
Artist iid : I08F  | Foreigner  | Album iid: I093  | No End In Sight_ The Very Best Of Foreig
real_path  : /media/rick/SANDISK128/Music/Foreigner/No End In Sight_ The Very Best Of Foreig/1-09 Dirty White Boy.m4a

self.playlist_paths[0]    : /media/rick/SANDISK128/Music/Boston/Boston_ Greatest Hits/05 Don't Look Back.m4a
self.playlist_paths[-1]   : /media/rick/SANDISK128/Music/The Police/The Police [Disc 2]/2-14 Tea In The Sahara.m4a
len(self.playlist_paths)  : 1441  | sys.get size of(self.playlist_paths): 13016

self.saved_selections[0]  : 422  | self.saved_selections[-1]: 3392
len(self.saved_selections): 1441  | sys.get size of(self.saved_selections): 13016

self.chron_attached[] is empty.

self.fake_paths[0]        : /media/rick/SANDISK128/Music/10cc/The Best of 10cc/01 Life Is A Minestrone.m4a
self.fake_paths[-1]       : /media/rick/SANDISK128/Music/White Zombie/Best Of 90s Rock Volume 2 - 20th Century/12 More Human Than Human.m4a
len(self.fake_paths)      : 3925  | sys.get size of(self.fake_paths): 33936

self.real_paths[0]        : /media/rick/SANDISK128/Music/10cc/The Best of 10cc/01 Life Is A Minestrone.m4a
self.real_paths[-1]       : /media/rick/SANDISK128/Music/White Zombie/Best Of 90s Rock Volume 2 - 20th Century/12 More Human Than Human.m4a
len(self.real_paths)      : 3925  | sys.get size of(self.real_paths): 33936

len(SORTED_LIST) Music Location Songs: 3925
len(self.lib_tree.tag_has("Artist")) : 163
len(self.lib_tree.tag_has("Album"))  : 292
len(self.lib_tree.tag_has("Title"))  : 3919

self.Xxx_ctl = FileControl() instances opened
==========================================================================================

Last file accessed - 'ffprobe' (self.play_ctl.metadata):
--------------------------------------------------------

INPUT #0 : mov,mp4,m4a,3gp,3g2,mj2, from '/media/rick/SANDISK128/Music/Foreigner/No End In Sight_ The Very Best Of Foreig/1-09 Dirty White Boy.m4a':
MAJOR_BRAND : M4A
MINOR_VERSION : 0
COMPATIBLE_BRANDS : M4A mp42isom
CREATION_TIME : 2013-04-17T13:05:41.000000Z
TITLE : Dirty White Boy
ARTIST : Foreigner
COMPOSER : De La Rock
ALBUM : No End In Sight: The Very Best Of Foreigner [Disc 1]
GENRE : Rock
TRACK : 9/16
DISC : 1/2
DATE : 1979
COMPILATION : 0
GAPLESS_PLAYBACK : 0
ENCODER : iTunes 11.0.2.26
ITUNSMPB : 00000000 00000840 00000100 0000000000944AC0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
ENCODING PARAMS : vers
ITUNNORM : 00001E4F 00002119 0000BE96 0000C032 0001CE0A 00032E83 00007E88 00007E88 000184EF 000186BF
ITUNES_CDDB_IDS : 16++
UFIDHTTP : //www.cddb.com/id3/taginfo1.html: 3CD3M103Q162081439U268A8220EC3A6DCAC7C9CEC67F47D21A87CP4
DURATION : 00:03:40.43, start: 0.047889, bitrate: 275 kb/s
AUDIO_RATE : 44100
BIT_RATE : (default)
STREAM #0:0[0X1](UND) : Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 270 kb/s (default)
CREATION_TIME(1) : 2013-04-17T13:05:41.000000Z
VENDOR_ID : [0][0][0][0]
STREAM #0:1[0X0] : Video: png, rgba(pc, gbr/unknown/unknown), 225x225, 90k tbr, 90k tbn (attached pic)

GLOBAL VARIABLES
==========================================================================================

START_DIR  : /media/rick/SANDISK128/Music/  | START_DIR.count(os.sep): 5
PRUNED_DIR : /media/rick/SANDISK128/Music/  | PRUNED_COUNT: 0
TV_APP_NAME: Firefox                      | TV_MONITOR: 0
TV_VOLUME  : 87                           | TV_MOVE_WINDOW: True
TV_BREAK1  : 90                           | TV_WINDOW_ANCHOR: center
TV_BREAK2  : 1080                         | TV_MOVE_WITH_COMPIZ: True
ENCODE_DEV : True                       
REW_FF_SECS: 10                           | REW_CUTOFF: 12
self.get_pending_cnt_total()              : 0
pending_apply() debug print flag DPRINT_ON: False
global_variables.py g.DEBUG_LEVEL         : 0
global_variables.py g.MUSIC_MIN_SIZE      : 100000
global_variables.py g.MUSIC_FILE_TYPES    : ['.aiff', '.caf', '.flac', '.mp3'] 
	 ['.mp4', '.m4a', '.oga', '.ogg', '.PCM', '.wav']
global_variables.py g.MSERVE_VERSION      : 3.5.0

SQL - Sqlite3 Information
==========================================================================================

SQL Sqlite3 Tables and Indices
--------------------------------------------------------------------------- 

#: 2  | Table  | Name: Music
	CREATE TABLE Music
		Id INTEGER PRIMARY KEY
		OsFileName TEXT
		OsAccessTime FLOAT
		OsModifyTime FLOAT
		OsChangeTime FLOAT
		OsFileSize INT
		ffMajor TEXT
		ffMinor TEXT
		ffCompatible TEXT
		Title TEXT
		Artist TEXT
		Album TEXT
		Compilation TEXT
		AlbumArtist TEXT
		AlbumDate TEXT
		FirstDate TEXT
		CreationTime TEXT
		DiscNumber TEXT
		TrackNumber TEXT
		Rating TEXT
		Genre TEXT
		Composer TEXT
		Comment TEXT
		Hyperlink TEXT
		Duration TEXT
		Seconds FLOAT
		GaplessPlayback TEXT
		PlayCount INT
		LastPlayTime FLOAT
		LyricsScore BLOB
		LyricsTimeIndex TEXT

#: 3  | Index  | Name: OsFileNameIndex
	CREATE UNIQUE INDEX OsFileNameIndex ON Music(OsFileName)

#: 4  | Table  | Name: History
	CREATE TABLE History
		Id INTEGER PRIMARY KEY
		Time FLOAT
		MusicId INTEGER
		User TEXT
		Type TEXT
		Action TEXT
		SourceMaster TEXT
		SourceDetail TEXT
		Target TEXT
		Size INT
		Count INT
		Seconds FLOAT
		Comments TEXT
		Timestamp FLOAT

#: 6  | Index  | Name: MusicIdIndex
	CREATE INDEX MusicIdIndex ON History(MusicId)

#: 7  | Index  | Name: TimeIndex
	CREATE INDEX TimeIndex ON History(Timestamp)

#: 9  | Index  | Name: TypeActionIndex
	CREATE INDEX TypeActionIndex ON History(Type, Action)

#: 10  | Table  | Name: Location
	CREATE TABLE Location
		Id INTEGER PRIMARY KEY
		Code TEXT
		Name TEXT
		ModifyTime FLOAT
		ImagePath TEXT
		MountPoint TEXT
		TopDir TEXT
		HostName TEXT
		HostWakeupCmd TEXT
		HostTestCmd TEXT
		HostTestRepeat INT
		HostMountCmd TEXT
		HostTouchCmd TEXT
		HostTouchMinutes INT
		Comments TEXT

#: 11  | Index  | Name: LocationCodeIndex
	CREATE UNIQUE INDEX LocationCodeIndex ON Location(Code)


SQL Blacklisted songs
--------------------------------------------------- 

0 : Compilations/Guardians of the Galaxy, Awesome Mix, Vol. 1/1-01 Hooked on a Feeling.m4a
1 : Compilations/Guardians of the Galaxy, Awesome Mix, Vol. 1/1-02 Go All the Way.m4a
2 : Compilations/Guardians of the Galaxy, Awesome Mix, Vol. 1/1-03 Spirit in the Sky.m4a
3 : Compilations/Guardians of the Galaxy, Awesome Mix, Vol. 1/1-04 Moonage Daydream.m4a
4 : Compilations/Guardians of the Galaxy, Awesome Mix, Vol. 1/1-05 Fooled Around and Fell in Love.m4a
5 : Compilations/Guardians of the Galaxy, Awesome Mix, Vol. 1/1-06 I’m Not in Love.m4a
6 : Compilations/Guardians of the Galaxy, Awesome Mix, Vol. 1/1-07 I Want You Back.m4a
7 : Compilations/Guardians of the Galaxy, Awesome Mix, Vol. 1/1-08 Come and Get Your Love.m4a
8 : Compilations/Guardians of the Galaxy, Awesome Mix, Vol. 1/1-09 Cherry Bomb.m4a
9 : Compilations/Guardians of the Galaxy, Awesome Mix, Vol. 1/1-10 Escape (The Piña Colada Song).m4a
10 : Compilations/Guardians of the Galaxy, Awesome Mix, Vol. 1/1-11 O‐o‐h Child.m4a
11 : Compilations/Guardians of the Galaxy, Awesome Mix, Vol. 1/1-12 Ain’t No Mountain High Enough.m4a
12 : Compilations/Guardians of the Galaxy_ Deluxe [Disc #1 of 2]/1-12 Ain’t No Mountain High Enough.m4a
13 : Compilations/Guardians of the Galaxy, Vol 1_ Awesome Mix, Vol. 1/1-12 Ain’t No Mountain High Enough.m4a
14 : April Wine/The Hits/01 - Say Hello.wav
15 : Nothing Has Changed [Disc 1]/01 Track 1.wav

SQL Whitelist substitutes
--------------------------------------------------- 

0 : Compilations/Guardians of the Galaxy, Vol. 1_ Awesome Mix, Vol. 1/1-01 Hooked on a Feeling.m4a
1 : Compilations/Guardians of the Galaxy, Vol. 1_ Awesome Mix, Vol. 1/1-02 Go All the Way.m4a
2 : Compilations/Guardians of the Galaxy, Vol. 1_ Awesome Mix, Vol. 1/1-03 Spirit in the Sky.m4a
3 : Compilations/Guardians of the Galaxy, Vol. 1_ Awesome Mix, Vol. 1/1-04 Moonage Daydream.m4a
4 : Compilations/Guardians of the Galaxy, Vol. 1_ Awesome Mix, Vol. 1/1-05 Fooled Around and Fell in Love.m4a
5 : Compilations/Guardians of the Galaxy, Vol. 1_ Awesome Mix, Vol. 1/1-06 I’m Not in Love.m4a
6 : Compilations/Guardians of the Galaxy, Vol. 1_ Awesome Mix, Vol. 1/1-07 I Want You Back.m4a
7 : Compilations/Guardians of the Galaxy, Vol. 1_ Awesome Mix, Vol. 1/1-08 Come and Get Your Love.m4a
8 : Compilations/Guardians of the Galaxy, Vol. 1_ Awesome Mix, Vol. 1/1-09 Cherry Bomb.m4a
9 : Compilations/Guardians of the Galaxy, Vol. 1_ Awesome Mix, Vol. 1/1-10 Escape (The Piña Colada Song).m4a
10 : Compilations/Guardians of the Galaxy, Vol. 1_ Awesome Mix, Vol. 1/1-11 O‐o‐h Child.m4a
11 : Compilations/Guardians of the Galaxy, Vol. 1_ Awesome Mix, Vol. 1/1-12 Ain't No Mountain High Enough.m4a
12 : None
13 : None
14 : April Wine/The Hits/01 Say Hello.wav
15 : None

SQL Whitelist reasons
--------------------------------------------------- 

0 : ('rename', True, 3961)
1 : ('rename', True, 3962)
2 : ('rename', True, 3963)
3 : ('rename', True, 3964)
4 : ('rename', True, 3965)
5 : ('rename', True, 3966)
6 : ('rename', True, 3967)
7 : ('rename', True, 3968)
8 : ('rename', True, 3969)
9 : ('rename', True, 3970)
10 : ('rename', True, 3971)
11 : ('rename', True, 3972)
12 : ('rename', True, 3973)
13 : ('rename', True, 3973)
14 : ('rename', True, 3844)
15 : ('partial', True, 0)

SQL Table Sizes
--------------------------------------------------------------------------- 

SQL Location Table 	Page Count:         3 	Size of pages:        3,072 
			Row Count:          6 	Last Row Number:          6 

SQL Music Table 	Page Count:     4,760 	Size of pages:    4,874,240 
			Row Count:      3,972 	Last Row Number:      3,972 

SQL History Table 	Page Count:     6,446 	Size of pages:    6,600,704 
			Row Count:     29,259 	Last Row Number:     30,901 

    History Rows:  |  Type='file'    |  Action='init'    |  count:    3,961
    History Rows:  |  Type='file'    |  Action='edit'    |  count:       80
    History Rows:  |  Type='meta'    |  Action='init'    |  count:    3,825
    History Rows:  |  Type='meta'    |  Action='edit'    |  count:   10,422
    History Rows:  |  Type='scrape'  |  Action='parm'    |  count:      298
    History Rows:  |  Type='lyrics'  |  Action='scrape'  |  count:    1,549
    History Rows:  |  Type='volume'  |  A='detect_old'   |  count:    3,950
    History Rows:  |  Type='volume'  |  A='loudnorm_1'   |  count:      767
    History Rows:  |  Type='volume'  |  A='loudnorm_2'   |  count:      767
    History Rows:  |  Type='volume'  |  A='detect_new'   |  count:      767
    History Rows:  |  Type='rename'  |  Action='Artist'  |  count:        0
    History Rows:  |  Type='rename'  |  Action='Album'   |  count:       16
    History Rows:  |  Type='rename'  |  Action='Title'   |  count:        1
    History Rows:  |  Type='rename'  |  Action='Other'   |  count:       13
    History Rows:  |  Type='delete'  |  Action='Artist'  |  count:        0
    History Rows:  |  Type='delete'  |  Action='Album'   |  count:        0
    History Rows:  |  Type='delete'  |  Action='Title'   |  count:       27
    History Rows:  |  Type='delete'  |  Action='Other'   |  count:        0

Pulse Audio - vu_pulse_audio.py PulseControl()
==========================================================================================

Pulse Audio - sink_input_list (sound sources)
--------------------------------------------------- 

sink: index=796L, mute=0, name=u'Simple DirectMedia Layer' ffplay
sink: index=798L, mute=0, name=u'Kennedy teams up with Trump. Elensky talks down to Modi. Donbass collapsing, Ukraine stuck in Kursk - YouTube' Firefox

Pulse Audio - sink_list (sound cards)
--------------------------------------------------- 

sink: description=u'GM204 High Definition Audio Controller Digital Stereo (HDMI)', index=0L, mute=0, name=u'alsa_output.pci-0000_01_00.1.hdmi-stereo', channels=2, volumes=[100% 100%]
sink: description=u'Built-in Audio Analog Stereo', index=1L, mute=1, name=u'alsa_output.pci-0000_00_1f.3.analog-stereo', channels=2, volumes=[100% 100%]

Pulse Audio - source_list (recording)
--------------------------------------------------- 

sink: description=u'Monitor of GM204 High Definition Audio Controller Digital Stereo (HDMI)', index=0L, mute=0, name=u'alsa_output.pci-0000_01_00.1.hdmi-stereo.monitor', channels=2, volumes=[100% 100%]
sink: description=u'Monitor of Built-in Audio Analog Stereo', index=1L, mute=0, name=u'alsa_output.pci-0000_00_1f.3.analog-stereo.monitor', channels=2, volumes=[100% 100%]
sink: description=u'Built-in Audio Analog Stereo', index=2L, mute=0, name=u'alsa_input.pci-0000_00_1f.3.analog-stereo', channels=2, volumes=[100% 100%]

Pulse Audio - card_list.profile_list
--------------------------------------------------- 

[<PulseCardProfileInfo at 7f6aeb5d3290 - available=1, description=u'Digital Stereo (HDMI) Output', n_sinks=1L, n_sources=0L, name=u'output:hdmi-stereo', priority=5400L>, <PulseCardProfileInfo at 7f6aeb5d3c50 - available=1, description=u'Digital Surround 5.1 (HDMI) Output', n_sinks=1L, n_sources=0L, name=u'output:hdmi-surround', priority=300L>, <PulseCardProfileInfo at 7f6aeb5d35d0 - available=1, description=u'Off', n_sinks=0L, n_sources=0L, name=u'off', priority=0L>]

Pulse Audio - pulse.server_info().default_sink_name
--------------------------------------------------- 

alsa_output.pci-0000_00_1f.3.analog-stereo

Loudness Normalization

Five steps are used for fastest performance, best results and granular control:

Sample Loudness Normalization


 much, much more to come 



Top ToS ToC Skip

Automatic Skin Color Based on Artwork

This video shows how the skin changes color:

The album artwork is automatically downloaded from the internet when the CD is encoded. You can also choose artwork from any website and use that instead.

The third pixel to the right and third pixel down set the skin tone when you resize the window that could yield a different skin tone!

Automatic Skin Color Notes


Image For Songs with No Artwork

This .gif (no sound) shows how a custom image is used when a song with no artwork is played:

No Art 5.gif
No Art 5.gif

Show below are the lines you need to change in the mserve.py Python script:

# When no artwork for song use this image file
ARTWORK_SUBSTITUTE = g.PROGRAM_DIR + "Be Creative 2 cropped.jpg"
# "Be Creative 2 cropped.png" is a 4.4 MB image 3120x3120

Copy your image file to the directory where you installed mserve.

NOTE:

The .gif video was created using:

mmm to get the window coordinates. However, you can use wmctrl -lG

byzanz-record -c --verbose --delay=1 --duration=5 --x=3668 --y=481 --width=1506 --height=737 "No Art 4.gif"

Then an on-line .gif optimizer reduced the file size from 33 MB to 22 MB using compression level 35 for “Lossy GIF”.


Top ToS ToC Skip

CD Encoding

Great lengths were taken to ensure currently playing song’s animations never lag even when CD’s are being encoded. This video shows how the music player keeps playing without any lag while a CD is being encoded:

Great lengths were also taken to ensure animations never lag even when focus grabbing dialog boxes require a response. If a focus grabbing dialog box is open when current song ends, the next song is played.

The encoding process uses libdiscid to read the Audio CD’s Disc ID. Then Musicbrainz is accessed to get track listings for Disc ID. It accesses Musicbrainz a second time to get the first recorded release date. It then grabs Album Artwork from the internet.

You can paste album artwork from the clipboard which you previously copied from Amazon, or another website.


How-To Encode a CD Overview

After inserting a CD, click the Rip CD button in the Music Location Tree window. The video below shows how to select encoding format, quality, naming format, album date, artwork, genre, comments and track level overrides to genre:

Encode CD Sample Video Highlights

Album Level Overrides Notes

Notes About Track Names in Bottom Pane


Encoding Metadata Tags

mserve Metadata Tags are displayed with common names. The common names follow the ffmpeg naming conventions:

ffmpeg TAG Description
TITLE Name of the Song
ARTIST Name of band or solo artist
ALBUM_ARTIST Same as ARTIST except for Compilations then “Various Artists”
ALBUM Name of the Album
COPYRIGHT Date the Album (not the song) was released
DISC Disc Number. E.G. single CD is “1/1”. 3 CD set could be “1/3”
TRACK_NUMBER E.G. When 12 tracks, first track “1/12”, last track “12/12”
DATE Song’s first release date in YYYY format. NOT the Album Date!
GENRE E.G. “Rock”, “Soundtrack”, “Country”, etc.
CREATION_TIME Date and time music file created (encoded)
COMPOSER When not specified, defaults to ARTIST
COMMENT One line comment
COMPILATION When value is “1”, folder is /Compilations/
GAPLESS_PLAYBACK “0” = Off, “1” = On. However, mserve doesn’t support it.
ENCODER E.G. “mserve 3.4.3” or “iTunes 11.4.0.18”
DISCID CDDB Free Disc ID
MUSICBRAINZ_DISC MusicBrainz Disc ID

Besides these Metadata tags, mserve SQL stores metadata for:

When mserve first discovers a song it takes a “snapshot” of the file’s:

When file times are updated by the Operating System, they are NOT refreshed in mserve SQL database. A side-effect of this is mserve can reset all files last access time when a program like Rhythm Box reads every song file and resets last access time to current time. See sql.py for examples of “fixing” stuff.


Renaming Artists, Albums and Song files After Encoding

Sometimes the on-line databases contain errors. For example, on a three CD Greatest Hits collection, you will find these Album names:

The last CD of the set doesn’t say [Disc 3] nor does it say Volume 3. It gets confusing when you are viewing the Music Location Tree window.

Use this Function to Rename, Not the File Manager

A file manager will not rename Artists, Albums and Song Titles within the mserve SQL database. If you rename with a file manager, original names will still be in the SQL database. So it is important to use this function instead of a file manager.

Call the function within the Music Location Tree window:

  1. Right click on the third CD Album (Greatest Hits of the 80’s).
  2. Select “Rename Album” from the popup menu.
  3. In the dialog box enter “Greatest Hits Of The 80’s [Disc 3]”
  4. Click the “Apply” button.
  5. A summary dialog box appears, as shown in the next section.

Rename Album Completion

mserve rename Greatest Hits Of The 80's [Disc 3].png
mserve rename Greatest Hits Of The 80's [Disc 3].png

Usage Notes:

Special Notes about ID3 Tags:


Substituting Special Characters in Filenames

When an Artist, Album or Song have these characters in the name:

The character will be replaced with the _ character instead. This is necessary to conform to operating system rules for directory and filenames.

For example, if the names with special characters are:

The file created in Linux will be:

The file created in Windows will be:

Note: In Linux the / character is used to separate directory levels. In Windows the \ character is used to separate directory levels.


Top ToS ToC Skip

Python Modules Dashboard

This dashboard is autogenerated when the website is refreshed. There are more dashboards in The Cookie Machine ⧉ for global Pippim Website maintenance.

Below are all the Python Modules in mserve. Note: Turn your mobile phone sideways (Landscape Mode) to see all columns.

Python Modules used in Pippim mserve Version 3.5.0

Python Module Lines Modified Description
calc.py 260 2024-03-21 16:50:20 Calculator for big numbers
disc_get.py 47 2023-07-16 23:16:08 Get discid of CD
encoding.py 4,224 2024-08-24 10:23:52 Encode (Rip) CD to music files
external.py 616 2024-06-02 11:17:26 Calls to External Programs
global_variables.py 233 2024-09-09 17:08:46 Global variables shared by all modules
image.py 1,891 2024-07-07 09:55:12 Image Processing
location.py 6,993 2024-09-15 08:26:53 Locations of Music Dirs & Devices
m 88 2024-04-28 18:47:25 m - Wrapper for mserve Fast Startup
mbz_get1.py 456 2024-06-02 13:46:53 Get musicbrainzngs ‘release-list’
mbz_get2.py 150 2024-06-02 13:35:25 Get musicbrainzngs artwork
message.py 1,880 2024-09-08 15:35:12 Message Dialog Boxes
monitor.py 1,043 2024-07-27 14:24:33 Multiple Monitor Management
mserve.py 23,796 2024-09-15 10:29:44 Main mserve Python Module
mserve_config.py 1,330 2024-09-15 08:27:15 Dependencies Checker and Setup
sql.py 5,492 2024-09-15 08:26:53 SQLite3 Interface
timefmt.py 270 2023-10-29 17:40:17 Date & Time formatting
toolkit.py 5,251 2024-09-12 05:27:45 Tkinter Tools and Tooltips()
vu_meter.py 202 2023-09-12 14:12:01 VU Meter processor spawned by mserve.py
vu_pulse_audio.py 1,001 2024-06-15 19:08:56 Pulse Audio Volume Controls
webscrape.py 1,487 2024-09-12 05:27:45 Webscrape Lyrics from genius.com
x11.py 313 2023-12-31 09:25:08 X11 window client
ALL Modules 57,023    

Table was updated September 15, 2024. The table is autogenerated when refresh.sh ⧉ 🔗 calls mserve_config.py ⧉ 🔗 which writes output to ~/website/programs/mserve_incl.md ⧉ 🔗

There are also some Bash scripts:


Dependencies

Below are the dependencies documented in mserve.py python program. You may already have them installed. The list is for Ubuntu 16.04 LTS using Python version 2.7.12. For Python 3+ versions, substitute python3 below where it says python:

sudo apt install compiz                  # for Hockey (smooth shark move)
sudo apt install dconf-editor            # for Hockey (gsettings)
sudo apt install ffmpeg                  # for artwork, ffprobe and ffplay
sudo apt install gstreamer1.0-tools      # For encoding CDs gst-launch-1.0
sudo apt install kid3                    # Optional for editing metadata
sudo apt install pauvcontrol             # For VU Meters (sound redirect)
sudo apt install pqiv                    # Make transparent Shark (Hockey)
sudo apt install python-appdirs          # Application directory names
sudo apt install python-beautifulsoup    # Scrape Song lyrics
sudo apt install python-gi               # Gnome window functions (newer)
sudo apt install gir1.2-wnck-3.0         # Gnome window functions (older?)
# NOTE: python-wnck not tested but may work instead of gi + gir1.2-wnck-3.0
sudo apt install python-libdiscid        # Get CD's disc ID
sudo apt install python-notify2          # Popup bubble messages
sudo apt install python-numpy            # Installed by default in Ubuntu
sudo apt install python-magic            # Get file type "magic" information
sudo apt install python-musicbrainzngs   # Get metadata for CD
sudo apt install python-mutagen          # Encode and ID3 tags
sudo apt install python-pil              # PIL graphics routines
sudo apt install python-pil.imagetk      # PIL image processing
sudo apt install python-pyaudio          # For background job vu_meter.py
sudo apt install python-requests         # Get Cover Art
sudo apt install python-selenium         # Automated YouTube Playlist play
sudo apt install python-subprocess32     # To compare locations
sudo apt install python-simplejson       # automatically installed Ubuntu
sudo apt install python-tk               # Tkinter (default in Windows & Mac)
sudo apt install wmctrl                  # To move Kid3 or Fishing window
sudo apt install x11-apps                # xwd window dump (screen shot)
sudo apt install xclip                   # Insert clipboard
sudo apt install xdotool                 # To move Kid3 or Fishing window

Additionally, there are external repositories (PPA) that need to be installed.


ffmpeg & ffprobe
============================================================================
The versions released with Ubuntu can be 8 years old. For example, In the
year 2024, Ubuntu 16.04 LTS ESM has ffmpeg version 2.8.17 from 2016. As of
April 2024, stable 6.1 versions of ffmpeg and ffprobe can be found at:

   https://ffmpeg.org/download.html

ffmpeg version 3.1 is minimum version for "loudnorm" filter processing. The
"loudnorm" filter is used to normalize maximum volume levels to 0 dB. You
can install ffmpeg and ffprobe to ~/bin and keep original versions in
/usr/bin.


External Repositories
============================================================================
sudo add-apt-repository ppa:j-4321-i/ttkwidgets  # CheckboxTreeview
# This is necessary for ttkwidgets and ttkcaldenar
sudo apt-get update
sudo apt-get install python-ttkwidgets           # CheckboxTreeview
sudo add-apt-repository ppa:j-4321-i/ppa
sudo apt-get update
sudo apt-get install python-tkcalendar

Finally, there are programs that have no sudo apt install capability such as pulsectl that require git pull command followed by cp command.

As of September 15, 2024, dependencies have to be manually installed. A long term plan is to create an installation script that installs all dependencies automatically. In the short term, development has begun to identify installed versions.


Top ToS ToC Skip

SQL Views

mserve uses proprietary data dictionary technology to quickly view rows in the three SQL tables:

SQL Location Table Data Dictionary Driven Viewer

The SQL Location Table defines the various music locations on your local device, external storage, remote hosts and mobile phones.

mserve uses proprietary data dictionary technology to quickly view rows in the SQL Location Table. Use the View dropdown menu from the Music Location Tree (main) window of mserve. A sample video appears below.

SQL Location Table Viewer Sample Video

SQL Table Viewer Sample Video Highlights

SQL Tables

The popular SQL database engine sqlite3 which is used by your web browser is also used by mserve.

Here are the SQL Tables and Indices that are created in the sqlite3 file ~/
/mserve/library.db:

def open_db(LCS=None):
    """ Open SQL Tables - Music Table and History Table
        Create Tables and Indices that don't exist
    :param LCS: instance of Location() class for lcs.open_code, etc.
    """

    #open_new_db()  # Database 'library_new.db' only used for conversions.

    global con, cursor, hist_cursor, loc_cursor, lcs
    if LCS:
        lcs = LCS  # Locations class

    con = sqlite3.connect(FNAME_LIBRARY)

    # MUSIC TABLE
    con.execute(
        "create table IF NOT EXISTS Music(Id INTEGER PRIMARY KEY, " +
        "OsFileName TEXT, OsAccessTime FLOAT, OsModifyTime FLOAT, " +
        "OsChangeTime FLOAT, OsFileSize INT, " +
        "ffMajor TEXT, ffMinor TEXT, ffCompatible TEXT, " +
        "Title TEXT, Artist TEXT, Album TEXT, Compilation TEXT, " +
        "AlbumArtist TEXT, AlbumDate TEXT, FirstDate TEXT, " +
        "CreationTime TEXT, DiscNumber TEXT, TrackNumber TEXT, " +
        "Rating TEXT, Genre TEXT, Composer TEXT, Comment TEXT, " +
        "Hyperlink TEXT, Duration TEXT, Seconds FLOAT, " +
        "GaplessPlayback TEXT, PlayCount INT, LastPlayTime FLOAT, " +
        "LyricsScore BLOB, LyricsTimeIndex TEXT)")

    con.execute("CREATE UNIQUE INDEX IF NOT EXISTS OsFileNameIndex ON " +
                "Music(OsFileName)")

    # HISTORY TABLE
    con.execute(
        "create table IF NOT EXISTS History(Id INTEGER PRIMARY KEY, " +
        "Time FLOAT, MusicId INTEGER, User TEXT, Type TEXT, " +
        "Action TEXT, SourceMaster TEXT, SourceDetail TEXT, " +
        "Target TEXT, Size INT, Count INT, Seconds FLOAT, " +
        "Comments TEXT, Timestamp FLOAT)")

    con.execute("CREATE INDEX IF NOT EXISTS MusicIdIndex ON " +
                "History(MusicId)")
    con.execute("CREATE UNIQUE INDEX IF NOT EXISTS TimeIndex ON " +
                "History(Timestamp)")
    con.execute("CREATE INDEX IF NOT EXISTS TypeActionIndex ON " +
                "History(Type, Action)")

    # LOCATION TABLE
    con.execute(
        "CREATE TABLE IF NOT EXISTS Location(Id INTEGER PRIMARY KEY, " +
        "Code TEXT, Name TEXT, ModifyTime FLOAT, ImagePath TEXT, " +
        "MountPoint TEXT, TopDir TEXT, HostName TEXT, " +
        "HostWakeupCmd TEXT, HostTestCmd TEXT, HostTestRepeat INT, " +
        "HostMountCmd TEXT, HostTouchCmd TEXT, HostTouchMinutes INT, " +
        "Comments TEXT)")
    con.execute("CREATE UNIQUE INDEX IF NOT EXISTS LocationCodeIndex ON " +
                "Location(Code)")


    ''' For mserve.py rename_file() function to rename "the" to "The" '''
    con.execute("PRAGMA case_sensitive_like = ON;")

    con.row_factory = sqlite3.Row
    cursor = con.cursor()
    hist_cursor = con.cursor()
    loc_cursor = con.cursor()

Pickled Data Files

The pickle data file format allows serialized Python objects such as variables, lists and dictionaries to be stored in non-serialized format on storage devices.

An abbreviation system is used for the filenames below:

For Windows:

For MacOS:

For Linux, ChromeOS, Windows Subsystem for Linux:

Here are the data files (stored in pickle format) created in the ~/.../mserve directory:

One subdirectory is created for every location. E.G. the subdirectory ~/.../mserve/L004 contains:

Within mserve Python scripts, lc.FNAME represents:

Note: When working inside the location.py module, drop the lc. prefix. In the other Python modules, import location as lc is used.

Pickled YouTube Playlists

The directory ~/.../mserve/YouTubePlaylists/ contains:

Generating YouTube Playlists requires some manual steps:

Copy 1

Before copy 1, open playlist in browser and press Ctrl + i . This will open the Console in the Chrome and Firefox web browsers.

let goToBottom = setInterval(() => window.scrollBy(0, 400), 1000)

Copy 1:

Note: You may have to adjust window height and/or console divider to split window between regular browser view and console view.

Copy 2

 clearInterval(goToBottom)
 console.log('\n'.repeat(50))

 let arrayVideos = []
 const links = document.querySelectorAll('a')
 for (const link of links) {
     if (link.id === "video-title") {
         link.href = link.href.split('&list=')[0]
         arrayVideos.push(link.title + ';' + link.href)
     }
 }

 let arrayTime = []
 const spans = document.querySelectorAll('span')
 for (const span of spans) {
     if (span.id === "text" &&
     span.classList.contains('ytd-thumbnail-overlay-time-status-renderer'))
     {
         arrayTime.push(span.innerText.replace(/[^\d:]/g, ''))
     }
 }

 if (arrayVideos.length === arrayTime.length) {
     for (var i=0; i<arrayVideos.length; i++) {
         arrayVideos[i] = arrayVideos[i] + ';' + arrayTime[i]
         console.log(arrayVideos[i])
     }
 }

Copy 2:

Copy 3

 let data = arrayVideos.join('\n')
 let blob = new Blob([data], {type: 'text/csv'})
 let elem = window.document.createElement('a')
 elem.href = window.URL.createObjectURL(blob)
 elem.download = 'my_data.csv'
 document.body.appendChild(elem)
 elem.click()
 document.body.removeChild(elem)

Copy 3:

Move CSV File

Put the following bash / shell commands into a script you can call. The sample file youPlaylistMoveCSV.sh can be copied and renamed.

#!/bin/bash
#  https://www.pippim.com/programs/mserve.html#pickled-youtube-playlists

#    Follow instructions and note "Copy Button" below:

#        STEP 1: Use CTRL+I in web browser
#        STEP 2: Click Button 1 to copy to clipboard "youPlayListScroll()"
#        STEP 3: Go to web browser and use CTRL+V then Enter
#        STEP 3A: Type "allow pasting" (without the quotes) if requested by browser
#        STEP 3B: Wait for web browser to stop scrolling, 1 second per song
#        STEP 4: Click Button 2 to copy to clipboard "youPlaylistCopy()"
#        STEP 5: Go to web browser and use Ctrl+V then Enter
#        STEP 6: Click Button 3 to copy to clipboard "youPlaylistSave()"
#        STEP 7: Go to web browser and use Ctrl+V then Enter
#        STEP 8: Run this bash script youPlaylistMoveCSV.sh
#        STEP 9: Use "View Playlists", select Playlist, View Button

if [ "$#" -ne 1 ]; then
    printf 'ERROR! You must provide the "Playlist Name" in quotes!\n' >&2
    exit 1
fi

if [ ! -f ~/Downloads/my_data.csv ]; then
    printf "ERROR! File ~/Downloads/my_data.csv not found!\n" >&2
    exit 1
fi

cd ~/Downloads
mv -v my_data.csv "$1".csv
cp -v "$1".csv ~/.local/share/mserve/YouTubePlaylists
rm -v ~/.local/share/mserve/YouTubePlaylists/"$1".pickle

Move CSV file:

Processing Playlists Methods


JSON Data Files

Just like pickle data files, the JSON format allows serialized Python objects like lists and dictionaries to be stored in non-serialized format on storage devices.

Unlike the pickle format, the JSON format is human-readable.

You will find JSON data files whenever a location resides on an FTP Host Server like an Android mobile phone.

These are the files you will find in the location subdirectory:


Top ToS ToC Skip

Windows Open Where You Want Them

Most applications always open their windows at the same locations. Then you have to move the windows to where you want them. mserve remembers where you like your windows to be and moves them there.

Besides the dozen or so windows that mserve uses, it also remembers if you use Hockey TV commercial buttons or FF & Rewind buttons instead. It also remembers if you prefer the Chronology (playlist) hidden or shown and the exact second of the last song you were listening to, or even if it was paused.

This is how mserve remembers and restores window positions and sizes:

def save_window_geom(name, geom):
    """
    CURRENT:
        Get geometry for window which was saved on last exit. If no record
        use 100,100 and predefined default width & height. Returns string
        of "width x height + x + y" with no spaces in between variables.
    """

    if sql.hist_check(0, 'window', name):
        sql.hist_cursor.execute("SELECT * FROM History WHERE Id = ?",
                                [sql.HISTORY_ID])
        d = dict(sql.hist_cursor.fetchone())
        if d is None:
            print('monitor.save_window_geom error no History ID:', HISTORY_ID)
            return False
    else:
        # First time add the record
        # sql.hist_add(time.time(), 0, lc.USER, 'window', name, geom,
        sql.hist_add(time.time(), 0, g.USER, 'window', name, geom,
                     'saved on exit, loaded on starting', None, 0, 0, 0.0,
                     "Used in conjunction with 'screen' History Record Id #")
        sql.con.commit()
        return True

    ''' We have the existing history record, simply replace the geometry field '''
    sql_cmd = "UPDATE History SET Time=?, SourceMaster=? WHERE Id = ?"

    sql.cursor.execute(sql_cmd, (time.time(), geom, sql.HISTORY_ID))
    sql.con.commit()

Top ToS ToC Skip

Tooltips Gradually Fade In and Out

A lot of work has gone into crafting the tooltips to delay before gradually fading in. Also, to gradually fade out. And finally, the Tooltip message bubble follows the moving mouse pointer.

Key features of tooltips:


Top ToS ToC Skip

Lyrics Synchronization

After song lyrics have been trained (Time Index assigned to each lyrics line) each line is highlighted as it is sung. Before training, lyrics are auto-scroll based on preset algorithm. Manual scrolling can be turn on to override Auto and Time scrolling.

A sample video is shown below. It shows how the toggle button works between automatic lyrics scrolling and manual scrolling:

Notes:

  1. When video starts with song in orange the default is “Auto Scrolling”
  2. Click button to engage “Manual Scroll”
  3. Now scroll bar appears on right, and you can scroll lyrics
  4. The vido changes to next song in black and the default is “Time Scrolling”
  5. Click the button to engage “Manual Scroll”
  6. Click the button again to reengage “Time Scrolling”
  7. Now each lyrics line is automatically highlighted as it is sung
  8. For Time Scrolling to work you need to train mserve with the timing.

Synchronized Lyrics in Action

This video shows how artwork, automatically obtained from the internet, is animated on your screen:

IMPORTANT: Un-mute video to hear song

This video also shows:

Additional notes:


Top ToS ToC Skip

Basic Time Synchronization

The ☰ (Hamburger) Dropdown Menu is shown below:

mserve lyrics hamburger menu.png
mserve lyrics hamburger menu.png

The same Dropdown menu appears when you right-click on the lyrics score (the song’s lyrics).


A sample video is shown below. It shows how the Basic Time Index feature works. Simply click to highlight and synchronize each lyrics line as it is being sung:

Basic Time Synchronization Sample Video

Basic Time Synchronization Sample Video Highlights

  1. The ☰ (Hamburger) Dropdown Menu where the Basic time index option is picked.
  2. Canceling the Basic time index option once started.
  3. Restarting the Basic time index from the ☰ menu.
  4. Clicking each line as it is sung.
  5. The time indices are automatically saved when the song ends, or you can choose the “Save index” option from the ☰ menu if, you don’t want to wait for the song to end.

Top ToS ToC Skip

Fine-Tune Time Index

Sometimes you just can’t seem to click at the right time using Basic Time Index in the previous section. For those cases the Fine-Tune Time Index window is provided.

Begin Sync option

In the following video notice how the option is included in the ☰ (Hamburger) menu and is selected. The video delays long enough so that you can see all the menu options.

This video shows:

  1. The Lyrics ☰ (Hamburger) Dropdown Menu options
  2. The “Ignore click” option on the menu. This closes the menu which is the same as moving the mouse off the menu and clicking outside the menu.
  3. Access the hamburger menu again and select the “Fine-tune time index” option
  4. The Fine-tune time-index window opens up and pauses the regular music player
  5. Select lyric lines in the lyrics score
  6. Begin sync button. As music plays you can click the line as it is sung
  7. Clicking each line as it is sung is the same behavior as the Basic time index function except that additional details are displayed
  8. Finally, the Fine-tune time index window is closed and regular music player resumes where it was interrupted

80% of lines must be basic synchronized

This screen appears when you have not completed basic time synchronization for at least 80% of the lines:

Basic Time Not Done.png
Basic Time Not Done.png

Fine-Tune Time Index cannot be done until 80% of lines have Basic Time Index completed.


Top ToS ToC Skip

Sample All option

The following video also shows how the Fine-tune time index function is selected. This time the video spends a little time showing you all the buttons in the function.

After turning on sound for the video below and clicking play, make sure you move your mouse outside the video. This way you can see the entire contents underneath your browser’s video control bar.

Video Highlights:

  1. The “Paused” graphic in the regular music player. It is programmatically generated and not an image file that can be changed.
  2. The regular music is resumed and the ☰ menu is used to select the Fine-tune time index option
  3. The Sample all button is selected
  4. The function plays the first second of each line
  5. We noticed at time index 154 seconds the instrumental section was left on too long. This caused the Chorus line and next line to start too late.
  6. Those three lines were selected and Begin sync button is used to fine-tune the timing
  7. When fixing the timing though we clicked too soon rather than
    too late as before. So we click back on a previous line and take a “mulligan”. Then we click again as the music catches up.
  8. Next, the time indices are saved by clicking the Save button
  9. Finally, the function is closed by clicking the Close button and the regular music player resumes play automatically.

Top ToS ToC Skip

Hide/Show Chronology

The Chronology Song List shows:

You can hide the Chronology Song List. This expands the art work and shifts the Lyrics Score beneath the Metadata and VU Meters. The sample video belows shows mserve playing Favorites playlist and the Hide Chronology button being clicked.

Hide Chronology List Sample Video

Hide Chronology List Sample Video Highlights

Note: The software used to create this video, made volume changes jump when volume slider was moved. Volume changes are “as smooth as silk”, when using mserve in real life.

Chronology List Popup Menu

Right-click on any song in the Chronology Song List to:

Chronology List Popup Menu Filter Options for Loudness Normalization

When a Loudness Normalization Playlist is active, the filter options change to:

You can redo volume normalization using the Music Location window’s Dropdown menu and selecting: ‘Tools’, ‘Volume’, ‘Analyze New Volume’, ‘Right Click’, ‘Redo normalization’. You do not need to run the ‘Create Normalization Playlist’ after redoing the normalization.


Top ToS ToC Skip

Locations

When you start mserve, or open and play a different location, music resumes playing where it left off. If music was paused, it is paused at the the same song position when mserve ended.

Locations are the heart of controlling mserve. Locations keep track of where music is stored.

In addition to tracking music on local storage, Locations can access music stored on a File Server or a Mobile Phone.


Sample View Locations Window

Here is a sample View Locations window with the selected location highlighted in green.

mserve View Locations.png
mserve View Locations.png

Mandatory Location Fields. The first two fields are mandatory:

The above sample screen was generated when optional host commands are not installed. 20,000 lines deep inside mserve, are these python tests:

self.nmap_installed = ext.check_command('nmap')
if self.nmap_installed:
   ''' Command 'nc' also required to quickly check if host is up '''
   self.nmap_installed = ext.check_command('nc')
self.ssh_installed = ext.check_command('ssh')
self.sshfs_installed = ext.check_command('sshfs')
if self.sshfs_installed:
   self.sshfs_installed = ext.check_command('fusermount')
self.wakeonlan_installed = ext.check_command('wakeonlan')

Sample Edit Location Window

Here is a sample Edit Location window with the selected location highlighted in green. This location is a “Sleeping Host”.

mserve Edit Location.png
mserve Edit Location.png

Mandatory Location Fields. The first two fields are mandatory:

The remaining fields, starting at Optional Remote Host Name, are optional.

Optional Location Fields:

If the File Server spends most of its life sleeping, mserve can wake it up with a “Magic Packet” over wired Ethernet. Then mserve keeps the host awake by “touching” a specific filename on the server. A special script called mserve_client.sh needs to be running on the host to keep it awake.


Android Wifi FTP Server Host

mserve Wifi FTP Server cropped.png
mserve Wifi FTP Server cropped.png

For wireless location synchronizing with an Android mobile phone, curlftpfs is used instead of sshfs. Older versions of mserve used sshfs but recently Android Wifi SSH Server stopped working on Android 10 mobile phone. On August 30, 2023, mserve was upgraded to use Wifi FTP Server instead.

These notes on Wifi FTP Server were quickly put together to meet August 31, 2023 mserve documentation deadline.

Make sure m or mserve.py is run from the command line when synchronizing phone locations. Extra messages are printed that do not appear in dialog boxes or the Information Centre

Install Wifi FTP Server ⧉ 🔗 from Google Play.

Initially you can accept the defaults for anonymous user and port 2221.

Grant access to the Music folder in Android. Android 10 requires you to press a weird icon to select SD Card instead of Internal Storage before selecting Music folder which exists on both medium.

The Wifi FTP Server screen will stay lit on your phone. Do not switch permanently to another Android App that will dim the screen. If you do, then mserve will report permission errors because host will be down. Of course this only applies when synchronizing. Normal phone usage doesn’t matter about Wifi FTP Server one way or the other.


In mserve Edit Location window go to the Command to wake up sleeping Host field and enter:

curlftpfs -o nonempty,uid=1000,gid=1000,umask=0022,user=android:android phone:2221 /mnt/phone

Above assumes local mount point is /mnt/phone and you have permissions to /phone directory but not /mnt directory which is owned by root.

Above assumes your /etc/hosts contains hostname phone with appropriate IP address such as 192.168.0.11 or whatever static IP address you assigned with the router software.

Above assumes your user id (not user name!) is 1000. Without this, files are mounted as owned by root.

The first time synchronization is run every file is diff checked which takes considerable time. Then modification time stamps are synchronized and subsequent synchronizations are 100 times faster. The first time it takes 1.5 hours to synchronize 4,000 songs over Wifi using curlftpfs. Subsequent synchronizations take 6 seconds for 4,000 songs (when there are no differences).

Known Problems with curlftpfs

curlftpfs chokes on filenames containing # of %.

Quote below from: JackSlateur / curlftpfs ⧉ 🔗

Note:

This is not the official project, which can be found there: http://curlftpfs.sourceforge.net/

I just added some code the correctly handle filename which contains url-special chars (actually, just # and %) by url-encoding them :

% -> %25

# -> %23

Using that, curl will not translate them, and will target the correct filename.

When curlftpfs chokes on a file, mserve transfers the file using ftp to compare the file to the current location.

Occasionally time-out errors are displayed in the console like this:

wait_for_cmd_output() 10 second time-out
Error on file: /mnt/phone/30 Seconds To Mars/A Beautiful Lie/10 A Modern Myth.m4a
Error: Permission denied on 'diff' check return code: 4

wait_for_cmd_output() 10 second time-out
Error on file: /mnt/phone/30 Seconds To Mars/A Beautiful Lie/11 The Battle Of One.m4a
Error: Permission denied on 'diff' check return code: 4

The reason is unknown, but when the next synchronization is run, the error doesn’t appear on the same files. To fix the error, the time-out was increased from 10 to 60 seconds. If more than 60 seconds is needed for the time-out, increase the value on line number 4034 in the file location.py:

if elapsed > 60.0:  # Aug 31/23 WiFi change 10.0 to 60.0 for `diff`

Optional Remote Host Support

There are two types of remote hosts supported:

Linux File Server

To debug keeping host awake, run the following commands on the host and client:

HOST - Open a terminal and enter command which runs forever:

mserve_client.sh -d

CLIENT - Open a terminal, and paste below, replacing <HOST> with Host name:

while : ; do ssh <HOST> "cat /tmp/mserve_client.log" ; sleep 60 ; done

When the commands fusermount, nmap, nc, ssh and sshfs are installed, extra fields appear for location details. See screenshots below.

Ensure Music files are mounted on the Host. If you’re using Windows iTunes, and need to mount in Linux, you may need something like:

sudo mount -t auto /dev/sdb1 /mnt/music

See: https://help.ubuntu.com/community/MountingWindowsPartitions

Make sure your SSH is using the standard port 22. Otherwise, you will have to open location.py and search on 22 > and 22/tcp.

Never use -odebug option for sshfs from mserve because it will lock up. Use debug only from command line for testing. mserve will not let you define this debug option.

More details: https://help.ubuntu.com/community/SSHFS

Android Mobile Phone

Older versions of mserve supported ssh on mobile but the Android application Wifi SSH Server no longer supports sshfs. In September 2023, mserve switched to FTP for mobile phones, with local filename caching.

Install Wifi FTP Server ⧉ 🔗 from Google Play.

More details on using FTP are above.

Initially FTP startup time was 5 minutes in mserve. Subsequently file-caching was utilized and startup time is now a few seconds for 4,000 music files.

Problems still exist using curlftpfs which hasn’t been updated in 9 years. For example, go to Location Music Tree (main window) and select the View Dropdown Menu. Then select the SQL Music Table option, and then the Update Metadata button. All files with # in the name are reported as: “1) Not a music file.”. These files:


Test Remote Host Status Display

These steps are followed when a remote host is tested:

Note: When synchronizing a remote host location, the same test is automatically run.

Sample Synchronize Location Test Host Video

The above video demonstrates:

Synchronize Location

When locations are synchronized, new files are NEVER added, and old files are NEVER deleted. If you would like to test the function first, review the script test_for_synch.sh.

Sample Synchronize Location Window

Below is a sample screen where the selected location is highlighted in green:

mserve Synchronize Location.png
mserve Synchronize Location.png

The steps below describe what happens when the “✔ Synchronize” button is clicked.

Synchronize Location Actions

A quick test is made to see if files have the same modification time and same size. If so the next file is checked.

If files have the same size but different times, the diff command is used to test every byte to see if they are different. If the files are identical, the modification times are set the same to the oldest modification time. The rationale is a new location was created, files were copied from an original location and the operating system reset the modification time to the current time.

If the files have the same time but different sizes or different contents, an error message is displayed because mserve has no clue which direction to copy files in.

If files are different, then the file with the newest modification time is copied over the oldest.

Before copying you are always given a chance to review action plans.

Below is an example of the action plan window:

mserve Synchronize Actions.png
mserve Synchronize Actions.png

The “Action” column states the reason for updating is that the file size has increased. This was due to album artwork being added to the source files.

Here’s a list of actions. All appear in “Action” column except those denoted as “hidden”:

The “Src”/”Source” location is the location currently opened in mserve. The “Trg”/”Target” location is the location that was picked to synchronize with.

The “OOPS” should never appear but is technically possible if another job is running that updates files in the Source or Target Location.

Note: For Remote Hosts, the linux cp command is used not the SSH scp command.

For Android mobile phones the last modification time (used to compare files) may not work properly. In this case, mserve creates a virtual modification time file to track what modification times should be.

Analyze Volume

ffmpeg is used to analyze volume levels. Two functions are provided:

Under Development.

Sample Analyze Volume Windows

Under Development.

Analyze Volume Actions

Under Development.


Top ToS ToC Skip

Playlists

Playlists are stored by location. Each location can have an unlimited number of playlists. Each playlist can have an unlimited number of songs, but only songs from the current music location.

Below is a sample Playlist Maintenance window from the Rename Playlist function:

Sample Rename Playlist Window

mserve Rename Playlist.png
mserve Rename Playlist.png

The Playlist name cannot be blank and must be unique per location. A warning is issued when the Playlist name has been used in another location.

The Playlist description is optional.

The Device location code is automatically assigned.

The columns for “Song Count”, “Size of Files” and “Duration” are automatically calculated as songs are selected and deselected in Music Location Tree checkboxes.

Five functions share the same Playlist Maintenance window:


YouTube Video with LRC Synchronized Lyrics

The screen below shows LRC (Synchronized LyRiCs) playing in mserve with the YouTube window dragged overtop.

mserve LRC Synchronized Lyrics
mserve LRC Synchronized Lyrics

NOTES:


mserve Smart Play YouTube Playlist

The screen below shows the mserve Smart Play Playlist window with a YouTube Playlist window next to it.

mserve YouTube Playlist compared to YouTube
mserve YouTube Playlist compared to YouTube

Top ToS ToC Skip

Hockey TV Commercial Buttons

When mserve starts, the Rewind 10 seconds and Fast Forward 10 seconds buttons are active. You can change these buttons to Stanley Cup Hockey Playoff TV Commercial buttons.

Basic Time Not Done.png
mserve hockey buttons.png

.gif (there is no sound) Highlights:

This .gif also shows how the Show/Hide Chronology button places song lyrics in a suitable location when the artwork size changes.

Using Hockey TV Commercial Button

This video shows what you see and hear when you click one of the Hockey TV Commercial Buttons.

Video Highlights:

Hockey TV Commercial Button Without compiz

This video shows what you see and hear when you click one of the Hockey TV Commercial Buttons and compiz code is commented out.

Hockey TV Commercial Sample Video Highlights


Although it doesn’t look as nice with the shark “jumping” between monitors, it works more reliably. To disable gsettings and the compiz option to disable place windows (which allows for smooth shark movement across monitors but leads to instability), open the file image.py and around line 1119 comment out the code:

# Removing "place" from gsettings allows smooth shark movement over
# monitors. However there are screen resets with disappearing windows
# for a couple seconds from time to time. Keeping "place" has shark
# stop at monitor border then "jump" into the next monitor.
'''
if "'place', " in self.old_compiz_plugins:
    self.place_in_plugins = True
    override = self.old_compiz_plugins.replace("'place', ", '')
    #print('override:', override)
    self.set_gsettings(override)
'''

mserve Volume During TV Commercials

This image shows mserve volume (ffplay) is at 60%:

hockey_volume.png
hockey_volume.png

When TV commercial ends, TV volume returns to 100% in 10 steps over 1/2 second. Music is paused in mserve and volume is reduced from 60% to 25% in 10 steps over 1/2 second.

As of June 11, 2023, mserve TV Commercial volume is initialized at 60% on line 401 in mserve.py:

TV_BREAK1 = 90          # Hockey TV commercial is 90 seconds
TV_BREAK2 = 1080        # Hockey TV intermission break is 18 minutes
TV_VOLUME = 60          # Hockey music play 66% of mserve volume level
TV_SOUND = "Firefox"    # Hockey broadcast is aired on Firefox browser

60% was found to be a suitable volume level for CBC broadcasts of the NHL Stanley Cup Playoffs. YMMV. Note that TV_VOLUME is a short name. A full name would be something like: “mserve volume when playing during hockey game muted TV commercials”.

FYI the “ALSA plug-in [python2.7]” sound processor is used by mserve to display the VU meters (vu_meter.py). Configuring the VU Meters using system output loopback to input stream is described in the next section.

Configure mserve Volume During Muted TV Commercials

To set the mserve volume during muted TV commercials, click the Edit dropdown menu and select Volume During TV Commercials:

mserve volume for tv commercials.png
mserve volume for tv commercials.png

This window changes the program variables shown in the last section.

Every location and every playlist within every location has a separate volume control for mserve when TV commercials are muted by mserve.


Top ToS ToC Skip

VU Meters

The VU Meters show in mserve need to be configured using pavucontrol (Pulse Audio Volume Control). Before we dive into pavucontrol though, the screenshot below shows how Ubuntu 16.04 displays sound output devices. This is for comparison purposes and to show you a screen you may already be familiar with.

Ubuntu 16.04 Sound Settings Panel.png
Ubuntu 16.04 Sound Settings Panel.png

In the example mserve application used on this webpage, the first output device called “GM204 High Definition Audio Controller” is usually used for sound output. This belongs to a 50” big screen Sony TV with good sound system including a sub-woofer. The soundcard (chipset) is located on a Nvidia 970M discrete graphics card.

The middle output device is “HDMI / DisplayPort - Built-in Audio”. This sound device is 43” 4K TCL / Google (Android) TV. The sound card is built into Intel Skylake (Thunderbolt 3) USB-C to HDMI converter plug.

The bottom output device is “Speakers - Built-in Audio”. These are sub-par speakers on a laptop. The only time they would be used if the laptop was unplugged from the local network (LAN). The soundcard is built into onboard Intel chipset.


pavucontrol Sound Output Loopback to Microphone

In order for VU Meters to work in mserve, The Pulse Audio Volume Control GUI application (pavucontrol) is used.

Pulse Audio Volume Control Sound Output

pavucontrol output devices.png
pavucontrol output devices.png

This screenshot shows the Pulse Audio Volume Control’s Output Devices Tab. In this example, the output was changed from the first device to the Built-in Audio speakers. The changes were made from the Ubuntu 16.04 Sound System Settings panel show in the previous section.

Notice the thick progress bar. It displays the sound playing on the output device and jumps very quickly. Progress bar activity is how you can confirm the active output device is selected.

When the output device is changed, the recording device must be changed for the VU meters to display the correct output sound device. (See the next section)


Pulse Audio Volume Control Recording Tab

pavucontrol recording tab.png
pavucontrol recording tab.png

This screenshot shows the Pulse Audio Volume Control’s Recording Tab.

Notice the thick progress bar at the bottom indicates no sound is being recorded. That is because it is listening to the wrong stream: “Monitor of GM204 High Definition Audio Controller Digital Stereo (HDMI)”. This stream is for the 50” Sony TV connected to Nvidia 970M card.

Remember in the last screenshot we used Ubuntu’s sound setting to change output from 50” TV to built-in laptop speakers.


Pulse Audio Volume Control Change Recording Source

The sample video shows the Pulse Audio Volume Control Recording Tab.

The stream “Built-in Audio Analog Stereo” is selected. Notice the thick progress shows sound volume level changes.

Then the stream name “Monitor of GM204 High Definition Audio Controller Digital Stereo (HDMI)” is selected.

When you change the output device loopback to recording; YOU MUST RESTART mserve. Otherwise the VU meters will merely be blank.

Credits and References:


Top ToS ToC Skip

Tools Dropdown Menu Examples

The Tools Dropdown Menu is found on the Music Location Tree Window.

These are the options in the Tools Dropdown Menu:

Sample Make LRC For Checked Songs Video

The Make LRC For Checked Songs feature will create an LRC (.lrc - synchronized lyrics) file for ever checked song in the Music Location Tree. This only applies to songs that have a lyrics score web scraped and where you have clicked on 80% of the lines to synchronize them.

Make LRC For Checked Songs Sample Video Highlights

NOTES:

Big Number Calculator Sample Video

Dealing with large numbers can make the mind numb. For example, the average drive of 1 Terabyte is 1000000000000 bytes. An average video of 4 Gigabytes is 4000000000. An average song of 8 Megabytes is 8000000 which is a little easier to type and read. However, it’s much easier with the Big Number Calculator.

In this video, we have 3,800 songs and we need to buy a USB stick to hold the music. What size should we buy?

Big Number Calculator Sample Video Highlights

NOTES:

Debug Information Sample Screen

mserve window off monitors.png
mserve window off monitors.png

This screenshot shows how a window can be outside a monitor’s viewable area.

Notice the window “Python3” is in the white area’s bottom central region. The white area is a “dead-zone” around the three monitors.

A window can enter the dead-zone when a monitor is disconnected from the computer.

Debug Information Sample Screen Notes

NOTES:

monitors.py Monitors().get_home_monitor(): x: 2261 y: 2740 w: 1300 h: 902 x2: 3561 y2: 3642

name: Python3

monitor.name: HDMI-0 monitor.x: 0 +y: 0 height: 1080 width: 1920 x2: 1920 y2: 1080

monitor.name: DP-1-1 monitor.x: 1920 +y: 0 height: 2160 width: 3840 x2: 5760 y2: 2160

monitor.name: eDP-1-1 monitor.x: 3870 +y: 2160 height: 1080 width: 1920 x2: 5790 y2: 3240

closest_x: eDP-1-1 closest_y: eDP-1-1

home_mon: Monitor(number=2, name='eDP-1-1', x=3870, y=2160, width=1920, height=1080, primary=True)

window: Window(number=92274698L, name='Python3', x=2261, y=2740, width=1300, height=902)

ERROR: Window is off screen at x + y: 2261 + 2740 x_cutoff: 5740 y_cutoff: 3190

Window(number=92274698L, name='Python3', x=2261, y=2740, width=1300, height=902)

Adjust to coordinates: 3920 + 2740

This screenshot shows how the window was forced back into the nearest monitor’s viewable area.


mserve fix window off monitors.png
mserve fix window off monitors.png

This screenshot shows how the window named “Python3” was moved into the third monitor on the lower right.

The window “Python3” is no longer in the “dead-zone”.

The process of moving windows out of the dead-zone is run when the Debug Information option is picked from the Tools Dropdown Menu


Top ToS ToC