Skip to content

Commit 4b5cc4f

Browse files
committed
Mp3PlayerGenerator
1 parent f6cc245 commit 4b5cc4f

File tree

1 file changed

+215
-0
lines changed

1 file changed

+215
-0
lines changed

utils/Mp3PlayerGenerator.java

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
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

Comments
 (0)