<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Egor Sledov</title>
    <description>The latest articles on Forem by Egor Sledov (@sledov).</description>
    <link>https://forem.com/sledov</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2812300%2F11f72e65-da0a-4a0b-bebf-9246827ea0b1.jpg</url>
      <title>Forem: Egor Sledov</title>
      <link>https://forem.com/sledov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/sledov"/>
    <language>en</language>
    <item>
      <title>Galene - a Simple Videoconferencing Server. Installation on VPS</title>
      <dc:creator>Egor Sledov</dc:creator>
      <pubDate>Wed, 27 Aug 2025 00:30:11 +0000</pubDate>
      <link>https://forem.com/sledov/galene-a-simple-videoconferencing-server-installation-on-vps-g4n</link>
      <guid>https://forem.com/sledov/galene-a-simple-videoconferencing-server-installation-on-vps-g4n</guid>
      <description>&lt;p&gt;Today I'll show you how to set up a simple videoconferencing server. You can use it to call relatives or friends.&lt;/p&gt;

&lt;p&gt;The server is called &lt;a href="https://galene.org" rel="noopener noreferrer"&gt;Galene&lt;/a&gt; (&lt;a href="https://github.com/jech/galene" rel="noopener noreferrer"&gt;github&lt;/a&gt;). It was originally developed during the pandemic at the University of Paris as a tool for online teaching. Over time, its functionality expanded, and now it's closer to Jitsi Meet in terms of features.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jitsi.org" rel="noopener noreferrer"&gt;Jitsi Meet&lt;/a&gt; is the most widely used open-source videoconferencing server. However, it is made up of many components, it is harder to install and needs much more powerful hardware to run. Galene, on the other hand, is written in Go in a minimalist style, and is essentially a single binary file that you can just run on a budget VPS (yes, you'll also need certificates and a couple of configuration files). According to the developers, it can even be run on a Raspberry Pi. I haven't tested that myself, though.&lt;/p&gt;

&lt;p&gt;I'll assume you want to make a video call right now, so we'll focus on how to get it working as quickly as possible. Later, we might need to adjust a few things.&lt;/p&gt;

&lt;p&gt;Here's the link to the official &lt;a href="https://galene.org/galene-install.html" rel="noopener noreferrer"&gt;installation instructions&lt;/a&gt;, but they take a different approach to life. So stick with me on this one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compiling Galene
&lt;/h2&gt;

&lt;p&gt;As you may have guessed, there is no ready-made binary – we'll build it ourselves now.&lt;/p&gt;

&lt;p&gt;I'll do this on a VPS (3 cores, 4GB RAM) with a fresh installation of Ubuntu 24.04.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;git golang
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command installs Git and the Go toolchain – compiler, standard library, and utilities.&lt;/p&gt;

&lt;p&gt;I'll keep the program and configuration in &lt;code&gt;~/apps/galene&lt;/code&gt;, and the source code in &lt;code&gt;~/src&lt;/code&gt;. So:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/src
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/src
git clone https://github.com/jech/galene.git
&lt;span class="nb"&gt;cd &lt;/span&gt;galene
&lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 go build &lt;span class="nt"&gt;-ldflags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'-s -w'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Go compiler will download additional modules and build a single self-contained executable file in the &lt;code&gt;galene&lt;/code&gt; directory without external dependencies.&lt;/p&gt;

&lt;p&gt;If everything goes well, you'll get a binary file about 10 MB in size.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lh&lt;/span&gt; galene
&lt;span class="nt"&gt;-rwxrwxr-x&lt;/span&gt; 1 sledov sledov 10M Aug 22 02:03 galene
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installation and First Run
&lt;/h2&gt;

&lt;p&gt;Let's move it to another directory and try running it there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/apps/galene
&lt;span class="nb"&gt;cp &lt;/span&gt;galene ~/apps/galene/
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/apps/galene/
./galene
2025/08/22 02:35:16 Group file ./groups/: lstat ./groups/: no such file or directory
2025/08/22 02:35:16 Starting built-in TURN server on :1194
2025/08/22 02:35:16 Relay &lt;span class="nb"&gt;test &lt;/span&gt;successful &lt;span class="k"&gt;in &lt;/span&gt;9.964835ms, RTT &lt;span class="o"&gt;=&lt;/span&gt; 69.67µs
^C2025/08/22 02:35:19 Stopping built-in TURN server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I stopped it with Ctrl-C.&lt;/p&gt;

&lt;p&gt;It runs, but clearly expects a "groups" directory. Galene relies on a specific directory structure. What it calls "groups" are really conference rooms, each set up with a small JSON configuration file.&lt;/p&gt;

&lt;p&gt;(We're now in &lt;code&gt;~/apps/galene&lt;/code&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir groups
mkdir &lt;/span&gt;data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The source code also includes a "static" directory containing the web interface written in vanilla JavaScript. We'll copy it as-is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; ~/src/galene/static &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating the First Group
&lt;/h2&gt;

&lt;p&gt;The first group we'll create will be called "family", and I'll be the admin there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano &lt;span class="nb"&gt;groups&lt;/span&gt;/family.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"sledov"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"secret"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"op"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is just the initial setup. Galene supports password hashing (e.g., with bcrypt), but for now this will do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Certificates from Let's Encrypt
&lt;/h2&gt;

&lt;p&gt;Now we need SSL certificates. Galene expects to find them in the "data" directory. We'll obtain them from Let's Encrypt and move them there.&lt;/p&gt;

&lt;p&gt;The Ubuntu repositories include certbot, a tool that automatically gets and renews free TLS certificates from Let’s Encrypt. Let's install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; certbot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the fully qualified domain name (FQDN) of our server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt;
galene.sledov.ru
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, the FQDN is galene.sledov.ru. Nothing is running at this address - it's just an example. but Galene has a &lt;a href="https://galene.org:8443" rel="noopener noreferrer"&gt;demo server&lt;/a&gt; you can try. For your installation, of course, it will be your own server's domain name.&lt;/p&gt;

&lt;p&gt;Now create the certificates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;certbot certonly &lt;span class="nt"&gt;--standalone&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; galene.sledov.ru
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A short interactive process follows: enter your email, then accept or decline the terms offered.&lt;/p&gt;

&lt;p&gt;Here's the key part of the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/galene.sledov.ru/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/galene.sledov.ru/privkey.pem
This certificate expires on 2025-11-20.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now have a certificate and key valid for about three months.&lt;/p&gt;

&lt;p&gt;Next, copy these files into the "data" directory and change their ownership:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;data
&lt;span class="nb"&gt;sudo cp&lt;/span&gt; /etc/letsencrypt/live/galene.sledov.ru/fullchain.pem cert.pem
&lt;span class="nb"&gt;sudo cp&lt;/span&gt; /etc/letsencrypt/live/galene.sledov.ru/privkey.pem key.pem
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;sledov:sledov &lt;span class="k"&gt;*&lt;/span&gt;.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running Galene
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/apps/galene
./galene
2025/08/22 04:00:54 Starting built-in TURN server on :1194
2025/08/22 04:00:54 Relay &lt;span class="nb"&gt;test &lt;/span&gt;successful &lt;span class="k"&gt;in &lt;/span&gt;12.505795ms, RTT &lt;span class="o"&gt;=&lt;/span&gt; 176.18µs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, Galene runs on port 8443. I won't show you the landing page where it asks to enter a group name, we'll go straight to the "family" group we created.&lt;/p&gt;

&lt;p&gt;xttps://galene.sledov.ru:8443/group/family/&lt;/p&gt;

&lt;p&gt;Here you'll need to enter your username and password, then allow the browser access to the camera and microphone. After that, you’ll see your camera feed in the conference window.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Invite People?
&lt;/h2&gt;

&lt;p&gt;On the left side, you'll find a panel with the roster and your name. To the right of your name there's a camera icon. Clicking it opens a menu where you need to select "Invite User".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr23w0cdb9dv3nvqd75j0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr23w0cdb9dv3nvqd75j0.jpg" alt="invite user" width="497" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then enter the name of the person you want to invite. Galene will generate a link that you can send, for example, via email.&lt;/p&gt;

&lt;p&gt;The link looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;_https://galene.sledov.ru:8443/group/family/?token=LBwxP4qboLA
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the recipient opens the link in a browser, he or she will not need to enter a username or password (but will still be required to allow camera and microphone access). The person will immediately join your room.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conference Characteristics
&lt;/h2&gt;

&lt;p&gt;Galene does not actively dial out to participants. They need to join the room/group themselves. You need to get used to this. You can use the text chat and drop a link to the video call there. You can also bookmark the URL or create a desktop shortcut and open it at the agreed time. You can also make an actual phone call and say "Join the group," or ring once and hang up - as long as you've agreed on that beforehand.&lt;/p&gt;

&lt;h2&gt;
  
  
  JavaScript Client
&lt;/h2&gt;

&lt;p&gt;If you don't like the standard interface or, for example, its layout, you can change it. The interface is written in vanilla JavaScript, and the code is relatively simple and readable, so you can tweak it to your liking.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;Hopefully by now you've explored the basic functionality and know how to make calls. I've zipped the "~/apps/galene" directory (without certificates, of course), and you can find it &lt;a href="https://drive.google.com/uc?export=download&amp;amp;id=1HaN1FvUkObd3En0czkyM-DuwTz6amkWe" rel="noopener noreferrer"&gt;here&lt;/a&gt;. You'll need to create your own certificates and run the "galene" binary. I highly recommend compiling Galene yourself. I haven't tested whether the application runs this way on distributions other than Ubuntu 24.04.&lt;/p&gt;

&lt;p&gt;If you have a firewall (I didn't in my case), you'll need to open ports TCP 8443, TCP 1194, UDP 1194, and a range of UDP ports, for example, 40000–40500. After that, you'll need to specify this range when starting the program as a command-line parameter, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./galene &lt;span class="nt"&gt;-udp-range&lt;/span&gt; 40000-40500
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want Galene to run permanently, it's best to create a "systemd" service unit. The &lt;a href="https://galene.org/galene-install.html" rel="noopener noreferrer"&gt;installation guide&lt;/a&gt; explains how to do this. It's also a good idea to configure it to run on port 443 instead of 8443 (hint: you don't need to proxy it through nginx for that).&lt;/p&gt;

&lt;p&gt;We've only covered the basics. The &lt;a href="https://galene.org/galene.html" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; provides detailed explanations – please explore it and experiment on your own.&lt;/p&gt;

&lt;p&gt;Happy videoconferencing! Feel free to ask questions.&lt;/p&gt;

</description>
      <category>videoconferencing</category>
      <category>zoom</category>
      <category>sysadmin</category>
      <category>ubuntu</category>
    </item>
    <item>
      <title>Lua scripting in mpv video player</title>
      <dc:creator>Egor Sledov</dc:creator>
      <pubDate>Tue, 04 Feb 2025 02:50:57 +0000</pubDate>
      <link>https://forem.com/sledov/lua-scripting-in-mpv-video-player-4fdh</link>
      <guid>https://forem.com/sledov/lua-scripting-in-mpv-video-player-4fdh</guid>
      <description>&lt;p&gt;Recently, I needed to cut a video fragment without re-encoding. I wasn't happy with how LosslessCut handled it, so I started looking for alternatives.&lt;/p&gt;

&lt;p&gt;On Reddit, I came across a &lt;a href="https://www.reddit.com/r/linux/comments/18xyzx9/comment/kgakryd/" rel="noopener noreferrer"&gt;post&lt;/a&gt; claiming that this could be done using the mpv-cut script. And this approach doesn't require an external GUI or additional programs.&lt;/p&gt;

&lt;p&gt;I checked it and the &lt;a href="https://github.com/familyfriendlymikey/mpv-cut" rel="noopener noreferrer"&gt;mpv-cut script&lt;/a&gt; turned out to be a Lua script that lets you cut a video fragment directly while watching it in the &lt;a href="https://mpv.io" rel="noopener noreferrer"&gt;mpv player&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hadn’t expected that the minimalist mpv player would support scripts that expand its functionality. I decided to check it out and see how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using mpv-cut
&lt;/h2&gt;

&lt;p&gt;Let's start with the script itself: how to install and use it.&lt;/p&gt;

&lt;p&gt;mpv has a configuration folder. On Linux, it's located at &lt;code&gt;~/.config/mpv&lt;/code&gt;, and on Windows at &lt;code&gt;%AppData%\Roaming\mpv&lt;/code&gt;. You need to create a &lt;code&gt;scripts&lt;/code&gt; directory in this folder (if it doesn't already exist) and clone the Git repository there:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;-b&lt;/span&gt; release &lt;span class="nt"&gt;--single-branch&lt;/span&gt; &lt;span class="s2"&gt;"https://github.com/familyfriendlymikey/mpv-cut.git"&lt;/span&gt; ~/.config/mpv/scripts/mpv-cut
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You also need to have &lt;a href="https://www.ffmpeg.org" rel="noopener noreferrer"&gt;ffmpeg&lt;/a&gt; installed and added to your system's PATH variable.&lt;/p&gt;

&lt;p&gt;Once this is set up, opening a file in mpv will automatically activate the script. When you reach the fragment you want to extract, press "c" on your keyboard to start copying. Press "c" again to stop copying. The video fragment will be saved in the same directory as the source file with a name like &lt;code&gt;COPY_1_video_FROM_00-00-07-457_TO_00-00-21-332.mkv&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can play the saved fragment to verify that all streams (audio, video, and subtitles) are intact—nothing was re-encoded; only a section was cut out.&lt;/p&gt;

&lt;p&gt;At this point, take a look at the main.lua file in the mpv-cut folder, but it may be too complex for a first script. So let’s start by writing a simple "Hello, World" script for mpv.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hello, World for the mpv Player
&lt;/h2&gt;

&lt;p&gt;Open your favorite text editor and create a file named &lt;code&gt;hello.lua&lt;/code&gt; in the &lt;code&gt;scripts&lt;/code&gt; folder. Enter the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;hello_world&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Hello, World!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_key_binding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"h"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"hello_world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hello_world&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don’t need to install Lua separately for mpv. The player has a built-in Lua interpreter.&lt;/p&gt;

&lt;p&gt;Since mpv has no graphical menu, you define functions and bind them to key presses. In this case, pressing the "h" key will print "Hello, World!" to the terminal.&lt;/p&gt;

&lt;p&gt;Here’s a &lt;a href="https://drive.google.com/file/d/1W25TUpyKt9xMsR9AmxOU692TswsoRL33/view?usp=sharing" rel="noopener noreferrer"&gt;sample video&lt;/a&gt; from the freely distributed &lt;em&gt;Sintel&lt;/em&gt; animation by Blender. I’ll later demonstrate how to display a list of subtitles using this video.&lt;/p&gt;

&lt;p&gt;You can test the script by playing any video and pressing "h" to check if "Hello, World!" appears in the terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subtitle List
&lt;/h2&gt;

&lt;p&gt;Now, let's create an example that retrieves information from the currently playing video. Instead of "Hello, World!", we’ll print a list of subtitles to the terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;show_subtitles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Subtitle List:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ID Language Format"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;track_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_property_native&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"track-list"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;track&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;ipairs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;track_list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;track&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"sub"&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;track&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;track&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;track&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;codec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;mp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_key_binding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"h"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"show_subtitles"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;show_subtitles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;mp.get_property_native("track-list")&lt;/code&gt; function in mpv returns a list of all tracks in the current video file. This includes video, audio, and subtitle tracks, along with their metadata such as ID, language, codec type, and other properties.&lt;/p&gt;

&lt;p&gt;This time, we bind the &lt;code&gt;show_subtitles&lt;/code&gt; function to the "h" key.&lt;/p&gt;

&lt;p&gt;The output looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[hello] Subtitle List:
[hello] ID Language Format
[hello] 1 ger subrip
[hello] 2 eng subrip
[hello] 3 spa subrip
[hello] 4 fre subrip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In mpv, the 'j' key (located right next to 'h') cycles through available subtitle tracks and displays information about the current track on screen. Press "h" and "j" several times to compare the output of our script with the built-in subtitle track switching function to make sure that our script works correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to Find More Information?
&lt;/h2&gt;

&lt;p&gt;I wanted simply to demonstrate that mpv supports Lua scripting and provide a couple of examples. Here are some resources to learn more about this.&lt;/p&gt;

&lt;p&gt;You can read about Lua scripting in mpv in the &lt;a href="https://mpv.io/manual/master/#lua-scripting" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Examples of Lua scripts included with the mpv package can be found on Linux at: &lt;code&gt;/usr/share/doc/mpv/examples/lua/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;These scripts are also available in the &lt;a href="https://github.com/mpv-player/mpv/tree/master/TOOLS/lua" rel="noopener noreferrer"&gt;mpv Git repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A large collection of user-contributed scripts can be found here:&lt;br&gt;
&lt;a href="https://github.com/mpv-player/mpv/wiki/User-Scripts" rel="noopener noreferrer"&gt;https://github.com/mpv-player/mpv/wiki/User-Scripts&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  On mpv's Customizability
&lt;/h2&gt;

&lt;p&gt;The flexibility and configurability of mpv are impressive, offering many possibilities for users.&lt;/p&gt;

&lt;p&gt;For example, when learning languages, it's convenient to display subtitles in two languages simultaneously (mpv supports this even without scripts). With scripts, you can do much more: create Anki cards from video content, perform simple editing and cropping, automatically switch tracks, and so on.&lt;/p&gt;

&lt;p&gt;Take another look at the user script collection - you might find a solution for your needs or inspiration for creating your own script.&lt;/p&gt;

</description>
      <category>lua</category>
      <category>programming</category>
      <category>linux</category>
      <category>videoediting</category>
    </item>
  </channel>
</rss>
