1
+ // Mp3PlayerGenerator
2
+ // Java 17+ is required
3
+ // Home Page: https://github.com/pponec/DirectoryBookmarks/blob/development/utils/Mp3PlayerGenerator.java
4
+ // License: Apache License, Version 2.0
5
+
6
+ package utils ;
7
+
8
+ import java .io .File ;
9
+ import java .nio .charset .Charset ;
10
+ import java .nio .charset .StandardCharsets ;
11
+ import java .nio .file .Files ;
12
+ import java .nio .file .Paths ;
13
+ import java .text .Normalizer ;
14
+ import java .util .*;
15
+ import java .util .regex .Pattern ;
16
+ import java .util .stream .Collectors ;
17
+
18
+ public class Mp3PlayerGenerator {
19
+
20
+ private final String homePage = "https://github.com/pponec/Mp3PlayerGenerator" ;
21
+ private final String appName = getClass ().getName ();
22
+ private final String appVersion = "1.3.3" ;
23
+ private final String outputFile = "index.html" ;
24
+ private final Locale locale = Locale .getDefault ();
25
+ private final Charset charset = StandardCharsets .UTF_8 ;
26
+
27
+ public static void main (String [] args ) throws Exception {
28
+ var o = new Mp3PlayerGenerator ();
29
+ if (args .length > 0 ) {
30
+ o .printHelpAndExit ();
31
+ }
32
+ var mp3List = o .getSoundFilesSorted ();
33
+ var html = o .buildHtmlPlayer (mp3List );
34
+ Files .writeString (Paths .get (o .outputFile ), html , o .charset );
35
+ }
36
+
37
+ void printHelpAndExit () {
38
+ System .out .println ("Script '%s' v%s (%s)" .formatted (appName , appVersion , homePage ));
39
+ System .out .println ("Usage version: %s" .formatted (appName ));
40
+ System .exit (1 );
41
+ }
42
+
43
+ CharSequence buildHtmlPlayer (List <String > mp3List ) {
44
+ var songFiles = mp3List .stream ()
45
+ .map (s -> "\" " + s + "\" " )
46
+ .collect (Collectors .joining ("\n \t , " ));
47
+ var params = Map .of (
48
+ "songFiles" , songFiles ,
49
+ "title" , getCurrentDirectoryName (),
50
+ "charset" , charset ,
51
+ "appName" , appName ,
52
+ "appVersion" , appVersion ,
53
+ "homePage" , homePage );
54
+ return format (htmlTemplate (), params );
55
+ }
56
+
57
+ /** Return all 'mp3' and 'ogg' files. */
58
+ List <String > getSoundFilesSorted () {
59
+ var files = new File ("." ).listFiles ();
60
+ if (files == null ) {
61
+ return Collections .emptyList ();
62
+ }
63
+ var filePattern = Pattern .compile ("\\ .(?i)(mp3|ogg)$" );
64
+ return Arrays .stream (files )
65
+ .filter (file -> file .isFile ())
66
+ .map (file -> file .getName ())
67
+ .filter (file -> filePattern .matcher (file ).find ())
68
+ .sorted (Comparator .comparing (file -> removeDiacritics (file )))
69
+ .toList ();
70
+ }
71
+
72
+ /** Remove diacritics and some common separator characters. */
73
+ String removeDiacritics (final String input ) {
74
+ var result = Normalizer .normalize (input , Normalizer .Form .NFD );
75
+ result = result .replaceAll ("\\ p{InCombiningDiacriticalMarks}+" , "" );
76
+ result = result .replaceAll ("[\\ -\\ +\\ .\\ |:;,_/ ]+" , " " );
77
+ result = result .toLowerCase (Locale .ENGLISH );
78
+ return result ;
79
+ }
80
+
81
+ String htmlTemplate () {
82
+ return """
83
+ <!DOCTYPE html>
84
+ <html>
85
+ <head>
86
+ <title>${title}</title>
87
+ <meta charset="${charset}"/>
88
+ <meta name="generator" content="${appName} v${appVersion}, ${homePage}"/>
89
+ <style>
90
+ h1 {
91
+ color: steelblue;
92
+ }
93
+ #playlist li {
94
+ cursor: pointer;
95
+ }
96
+ #playlist li.current {
97
+ font-weight: bold;
98
+ }
99
+ #repeater {
100
+ margin-left: 17px;
101
+ }
102
+ #audioPlayer {
103
+ width: 100%;
104
+ border: none;
105
+ }
106
+ .footer, .footer a {
107
+ margin-top: 30px;
108
+ margin-left: 7px;
109
+ font-style: italic;
110
+ font-size: 0.9rem;
111
+ color: Gray;
112
+ }
113
+ </style>
114
+ </head>
115
+ <body>
116
+ <h1>${title}</h1>
117
+ <ol id="playlist"></ol>
118
+ <label id="repeater">File repeater:
119
+ <input type="checkbox"/>
120
+ </label>
121
+ <audio id="audioPlayer" controls></audio>
122
+ <p id="currentSong"></p>
123
+ <script>
124
+ var playlist = [ ${songFiles} ];
125
+ var audioPlayer = document.getElementById('audioPlayer');
126
+ var playlistElement = document.getElementById('playlist');
127
+ var currentSongIndex = 0;
128
+ var currentSongElement = document.getElementById('currentSong');
129
+ var repeater = document.querySelector('#repeater input');
130
+
131
+ // Generate playlist:
132
+ playlist.forEach(function(song, index) {
133
+ var listItem = document.createElement('li');
134
+ listItem.innerText = 'file: ' + playlist[index];
135
+ listItem.onclick = function() {
136
+ playSong(index);
137
+ // Disable a scrolling to the audio player:
138
+ var currentPosition = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
139
+ audioPlayer.focus();
140
+ window.scrollTo(0, currentPosition);
141
+ };
142
+ playlistElement.appendChild(listItem);
143
+ });
144
+
145
+ function playSong(index) {
146
+ currentSongIndex = index;
147
+ var songUrl = playlist[index];
148
+ audioPlayer.src = encodeURI(songUrl);
149
+ audioPlayer.play();
150
+ updateCurrentSongText();
151
+ updatePlaylistHighlight();
152
+ }
153
+
154
+ audioPlayer.addEventListener('ended', function() {
155
+ if (!repeater.checked) {
156
+ currentSongIndex = (currentSongIndex + 1) % playlist.length
157
+ }
158
+ playSong(currentSongIndex);
159
+ });
160
+
161
+ function updateCurrentSongText() {
162
+ var title = 'Now playing ' + (currentSongIndex + 1) + ". file: " + playlist[currentSongIndex];
163
+ currentSongElement.innerText = title;
164
+ }
165
+
166
+ function updatePlaylistHighlight() {
167
+ var playlistItems = playlistElement.getElementsByTagName('li');
168
+ for (var i = 0; i < playlistItems.length; i++) {
169
+ if (i === currentSongIndex) {
170
+ playlistItems[i].classList.add('current');
171
+ } else {
172
+ playlistItems[i].classList.remove('current');
173
+ }
174
+ }
175
+ }
176
+ </script>
177
+ <div class="footer">
178
+ Generated by the <a href="https://github.com/pponec/Mp3PlayerGenerator">${appName}</a> version ${appVersion}.
179
+ </div>
180
+ </body>
181
+ </html>
182
+ """ .stripIndent ();
183
+ }
184
+
185
+ /** Join a template with arguments (a method from an Ujorm framework) */
186
+ final CharSequence format (String msg , Map <String , ?> args ) {
187
+ if (msg == null || args == null ) {
188
+ return String .valueOf (msg );
189
+ }
190
+ final var begTag = "${" ;
191
+ final var endTag = '}' ;
192
+ final var result = new StringBuilder (32 + msg .length ());
193
+ int i , last = 0 ;
194
+ while ((i = msg .indexOf (begTag , last )) >= 0 ) {
195
+ final var end = msg .indexOf (endTag , i );
196
+ final var key = msg .substring (i + begTag .length (), end );
197
+ final var val = args .get (key );
198
+ if (val != null ) {
199
+ result .append (msg , last , i ).append (val );
200
+ } else {
201
+ result .append (msg , last , end + 1 );
202
+ }
203
+ last = end + 1 ;
204
+ }
205
+ return result .append (msg , last , msg .length ());
206
+ }
207
+
208
+ String getCurrentDirectoryName () {
209
+ try {
210
+ return Paths .get ("" ).toAbsolutePath ().getFileName ().toString ();
211
+ } catch (RuntimeException e ) {
212
+ return "MP3 player" ;
213
+ }
214
+ }
215
+ }
0 commit comments