1{ lib, ... }:
2let
3 ocrContent = "Music Test";
4 musicFileName = "Example";
5 musicFile = "${musicFileName}.mp3";
6
7 ocrPauseColor = "#FF00FF";
8 ocrStartColor = "#00FFFF";
9in
10{
11 name = "lomiri-music-app-standalone";
12 meta.maintainers = lib.teams.lomiri.members;
13
14 nodes.machine =
15 { config, pkgs, ... }:
16 {
17 imports = [
18 ./common/auto.nix
19 ./common/user-account.nix
20 ./common/x11.nix
21 ];
22
23 services.xserver.enable = true;
24
25 environment = {
26 # Setup video
27 etc."${musicFile}".source =
28 pkgs.runCommand musicFile
29 {
30 nativeBuildInputs = with pkgs; [
31 ffmpeg # produce music
32 (imagemagick.override { ghostscriptSupport = true; }) # produce OCR-able cover image
33 ];
34 }
35 ''
36 magick -size 400x400 canvas:white -pointsize 40 -fill black -annotate +100+100 '${ocrContent}' output.png
37 ffmpeg -re \
38 -f lavfi -i anullsrc=channel_layout=mono:sample_rate=44100 \
39 -i output.png \
40 -map 0:0 -map 1:0 \
41 -id3v2_version 3 \
42 -metadata:s:v title="Album cover" \
43 -metadata:s:v comment="Cover (front)" \
44 -t 120 \
45 $out -loglevel fatal
46 '';
47
48 systemPackages =
49 with pkgs;
50 [
51 xdg-user-dirs # generate XDG dirs
52 xdotool # mouse movement
53 ]
54 ++ (with pkgs.lomiri; [
55 mediascanner2
56 lomiri-music-app
57 lomiri-thumbnailer
58 # To check if playback actually works, or is broken due to missing codecs, we need to make the app's icons more OCR-able
59 (lib.meta.hiPrio (
60 suru-icon-theme.overrideAttrs (oa: {
61 # Colour the background in special colours, which we can OCR for
62 postPatch = (oa.postPatch or "") + ''
63 substituteInPlace suru/actions/scalable/media-playback-pause.svg \
64 --replace-fail 'fill:none' 'fill:${ocrPauseColor}'
65
66 substituteInPlace suru/actions/scalable/media-playback-start.svg \
67 --replace-fail 'fill:none' 'fill:${ocrStartColor}'
68 '';
69 })
70 ))
71 ]);
72
73 variables = {
74 UITK_ICON_THEME = "suru";
75 };
76 };
77
78 # Get mediascanner-2.0.service
79 services.desktopManager.lomiri.enable = lib.mkForce true;
80
81 # ...but stick with icewm
82 services.displayManager.defaultSession = lib.mkForce "none+icewm";
83
84 systemd.tmpfiles.settings = {
85 "10-lomiri-music-app-test-setup" = {
86 "/root/Music".d = {
87 mode = "0755";
88 user = "root";
89 group = "root";
90 };
91 "/root/Music/${musicFile}"."C+".argument = "/etc/${musicFile}";
92 };
93 };
94
95 i18n.supportedLocales = [ "all" ];
96 };
97
98 enableOCR = true;
99
100 testScript = ''
101 from collections.abc import Callable
102 import tempfile
103 import subprocess
104
105 pauseColor: str = "${ocrPauseColor}"
106 startColor: str = "${ocrStartColor}"
107
108 # Based on terminal-emulators.nix' check_for_pink
109 def check_for_color(color: str) -> Callable[[bool], bool]:
110 def check_for_color_retry(final=False) -> bool:
111 with tempfile.NamedTemporaryFile() as tmpin:
112 machine.send_monitor_command("screendump {}".format(tmpin.name))
113
114 cmd = 'convert {} -define histogram:unique-colors=true -format "%c" histogram:info:'.format(
115 tmpin.name
116 )
117 ret = subprocess.run(cmd, shell=True, capture_output=True)
118 if ret.returncode != 0:
119 raise Exception(
120 "image analysis failed with exit code {}".format(ret.returncode)
121 )
122
123 text = ret.stdout.decode("utf-8")
124 return color in text
125
126 return check_for_color_retry
127
128 machine.wait_for_x()
129
130 # mediascanner2 needs XDG dirs to exist
131 machine.succeed("xdg-user-dirs-update")
132
133 # mediascanner2 needs to have run, is only started automatically by Lomiri
134 machine.systemctl("start mediascanner-2.0.service", "root")
135
136 with subtest("lomiri music launches"):
137 machine.succeed("lomiri-music-app >&2 &")
138 machine.wait_for_console_text("Queue is empty")
139 machine.sleep(10)
140 machine.send_key("alt-f10")
141 machine.sleep(2)
142 machine.wait_for_text("favorite music")
143 machine.screenshot("lomiri-music")
144
145 with subtest("lomiri music plays music"):
146 machine.succeed("xdotool mousemove 30 720 click 1") # Skip intro
147 machine.sleep(2)
148 machine.wait_for_text("Albums")
149 machine.succeed("xdotool mousemove 25 45 click 1") # Open categories
150 machine.sleep(2)
151 machine.wait_for_text("Tracks")
152 machine.succeed("xdotool mousemove 25 240 click 1") # Switch to Tracks category
153 machine.sleep(2)
154 machine.wait_for_text("${musicFileName}") # the test file
155 machine.screenshot("lomiri-music_listing")
156
157 # Ensure pause colours isn't present already
158 assert (
159 check_for_color(pauseColor)(True) == False
160 ), "pauseColor {} was present on the screen before we selected anything!".format(pauseColor)
161
162 machine.succeed("xdotool mousemove 25 120 click 1") # Select the track
163
164 # Waiting for pause icon to be displayed
165 with machine.nested("Waiting for the screen to have pauseColor {} on it:".format(pauseColor)):
166 retry(check_for_color(pauseColor))
167
168 machine.screenshot("lomiri-music_playback")
169
170 # Ensure play colours isn't present already
171 assert (
172 check_for_color(startColor)(True) == False
173 ), "startColor {} was present on the screen before we were expecting it to be!".format(startColor)
174
175 machine.succeed("xdotool mousemove 860 480 click 1") # Pause track (only works if app can actually decode the file)
176
177 # Waiting for play icon to be displayed
178 with machine.nested("Waiting for the screen to have startColor {} on it:".format(startColor)):
179 retry(check_for_color(startColor))
180
181 machine.screenshot("lomiri-music_paused")
182
183 # Lastly, check if generated cover image got extracted properly
184 # if not, indicates an issue with mediascanner / lomiri-thumbnailer
185 machine.wait_for_text("${ocrContent}")
186
187 machine.succeed("pkill -f lomiri-music-app")
188
189 with subtest("lomiri music localisation works"):
190 machine.succeed("env LANG=de_DE.UTF-8 lomiri-music-app .mp4 >&2 &")
191 machine.wait_for_console_text("Restoring library queue")
192 machine.sleep(10)
193 machine.send_key("alt-f10")
194 machine.sleep(2)
195 machine.wait_for_text("Titel")
196 machine.screenshot("lomiri-music_localised")
197 '';
198}