Skip to content

madoslinux/mados-video-player

Repository files navigation

madOS Video Player

A minimalist GTK3 video player using GStreamer for playback, featuring a floating controls interface, playlist management, and session persistence via SQLite.

Features

  • Floating Controls: Ultra-minimalist interface with controls that appear on hover
  • Playlist Management: Add files, directories, shuffle, and repeat modes
  • Session Persistence: Automatically saves and restores last playlist
  • Keyboard Shortcuts: Space (play/pause), F/F11 (fullscreen), arrows (seek/volume)
  • Drag & Drop: Drop video files directly onto the window
  • Multi-format Support: MP4, MKV, AVI, MOV, WEBM, and many more

Requirements

  • Python 3
  • GTK3
  • GStreamer 1.0 + GStreamer Python bindings
  • SQLite3 (built-in)

Installation

# Run with Python module
python -m mados_video_player

# Or use the executable
./mados-video-player

# Or run directly
python __main__.py

Architecture

Frontend (GTK3 UI)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      VideoPlayerApp                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  Overlay    β”‚  β”‚ Video       β”‚  β”‚ Floating         β”‚   β”‚
β”‚  β”‚  Container  β”‚  β”‚ Display     β”‚  β”‚ Controls         β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚                    β”‚                    β”‚
         β–Ό                    β–Ό                    β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    PlaylistWindow                           β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  TreeView with playlist items                       β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Backend (Core Modules)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  player.py - GStreamer Engine                               β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  PlayerEngine: play, pause, stop, seek, volume     β”‚   β”‚
β”‚  β”‚  Video sink detection (Wayland/X11/GTK)             β”‚   β”‚
β”‚  β”‚  Callbacks: on_eos, on_error, on_state, on_positionβ”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  playlist.py - Playlist Management                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  Playlist: add_file, add_directory, next, previous β”‚   β”‚
β”‚  β”‚  Repeat modes: NONE, ALL, ONE                       β”‚   β”‚
β”‚  β”‚  Shuffle support                                    β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  database.py - SQLite Persistence                           β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  PlaylistDB: save/load playlists, session state    β”‚   β”‚
β”‚  β”‚  Tables: playlists, playlist_items, session         β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Sequence Diagrams

Frontend Flow: Play Video

sequenceDiagram
    participant User
    participant UI as VideoPlayerApp
    participant Engine as PlayerEngine
    participant Playlist as Playlist
    participant Display as Video Display

    User->>UI: Double-click / Play button
    UI->>Playlist: get current file
    Playlist-->>UI: filepath
    UI->>Engine: load(filepath)
    Engine->>Engine: Set GStreamer URI
    Engine-->>UI: True (loaded)
    UI->>Engine: play()
    Engine->>Display: Start video output
    Engine->>UI: on_state("playing")
    UI->>UI: Update play button to ⏸

    loop Playback
        Engine->>UI: on_position(ns)
        UI->>UI: Update seek slider
    end

    Engine->>UI: on_eos()
    UI->>Playlist: next()
    Playlist-->>UI: next filepath
    UI->>Engine: load(filepath)
    UI->>Engine: play()
Loading

Frontend Flow: Playlist Management

sequenceDiagram
    participant User
    participant UI as VideoPlayerApp
    participant Window as PlaylistWindow
    participant Playlist as Playlist
    participant DB as PlaylistDB

    User->>UI: Menu > Playlist
    UI->>Window: Create PlaylistWindow
    Window->>Playlist: _refresh()
    Playlist-->>Window: items list
    Window->>Window: Populate TreeView

    User->>Window: Double-click item
    Window->>Playlist: select(index)
    Playlist-->>Window: filepath
    Window->>UI: _play_current()
    UI->>UI: Engine.load() + play()

    User->>Window: Click delete
    Window->>Playlist: remove(index)
    Playlist-->>Window: updated items
    Window->>Window: _refresh()
    Window->>DB: save_session_playlist()
Loading

Backend Flow: Seek Operation

sequenceDiagram
    participant UI
    participant Engine as PlayerEngine
    participant Gst as GStreamer
    participant Pipeline as Pipeline

    UI->>UI: _on_seek_changed()
    UI->>UI: _do_debounced_seek(50ms debounce)
    UI->>Engine: seek(position_ns)

    alt Pipeline not ready
        Engine->>Pipeline: set_state(PAUSED)
    end

    Engine->>Pipeline: seek(speed, TIME, FLUSH, SET, position)
    Pipeline-->>Engine: seek result
    Engine-->>UI: Return

    Note over Engine,Pipeline: If was playing, stay playing<br/>If was paused, stay paused
Loading

Backend Flow: Session Persistence

sequenceDiagram
    participant App
    participant Playlist as Playlist
    participant DB as PlaylistDB
    participant SQLite as SQLite DB

    Note over App,SQLite: On Startup (restore_session)
    App->>DB: load_session_playlist()
    DB->>SQLite: Query session + playlist
    SQLite-->>DB: Session data
    DB-->>App: {filepaths, current_index, repeat_mode, shuffle}
    App->>Playlist: items.extend(filepaths)
    App->>Playlist: current_index = ...
    App->>Playlist: repeat_mode = ...

    Note over App,SQLite: On Close (_on_destroy)
    App->>Playlist: get current state
    App->>DB: save_session_playlist(filepaths, index, repeat, shuffle)
    DB->>SQLite: INSERT/UPDATE playlists + session
    SQLite-->>DB: Success
    DB-->>App: Return
Loading

Backend Flow: Video Sink Detection

sequenceDiagram
    participant Engine as PlayerEngine
    participant Gst as GStreamer
    participant Display as Gdk.Display

    Engine->>Engine: _setup_pipeline()
    Engine->>Gst: ElementFactory.make("playbin")

    alt gtksink available (preferred)
        Engine->>Gst: ElementFactory.make("gtksink")
        Engine->>Engine: Use gtksink widget
    else Wayland display
        Engine->>Display: get_default()
        Display-->>Engine: WaylandDisplay
        Engine->>Gst: Try gtkglsink, then waylandsink
    else X11 display
        Engine->>Display: get_default()
        Display-->>Engine: X11Display
        Engine->>Gst: Try xvimagesink, fallback to ximagesink
    else Other
        Engine->>Gst: Use autovideosink
    end

    Engine->>Gst: pipeline.set_property("video-sink", sink)
    Engine->>Gst: bus.add_signal_watch()
    Engine->>Engine: Connect bus callbacks
Loading

Keyboard Shortcuts

Key Action
Space Play/Pause
F / F11 Toggle Fullscreen
Escape Exit Fullscreen
Left/Right Seek -10s / +10s (Ctrl for 60s)
Up/Down Volume Β±5%
M Toggle Mute
N Next track
P Previous track
Ctrl+O Open file
Ctrl+Q Quit

File Structure

mados-video-player/
β”œβ”€β”€ __init__.py         # Package metadata
β”œβ”€β”€ __main__.py         # Entry point
β”œβ”€β”€ app.py              # Main window (GTK)
β”œβ”€β”€ player.py           # GStreamer engine
β”œβ”€β”€ playlist.py         # Playlist logic
β”œβ”€β”€ database.py         # SQLite persistence
β”œβ”€β”€ theme.py            # Nord CSS theme
β”œβ”€β”€ translations.py     # i18n strings
β”œβ”€β”€ .gitignore
β”œβ”€β”€ AGENTS.md          # Developer guidelines
└── README.md

License

MIT License

About

Video player for mados

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages