Stories
Slash Boxes
Comments

SoylentNews is people

Log In

Log In

Create Account  |  Retrieve Password


Zinnia Zirconium (11163)

The Fine Print: The following are owned by whoever posted them. We are not responsible for them in any way.
Saturday July 25, 20
06:28 AM
Security

So yeah. Trolls convinced me to Let's Encrypt. Everybody has to Let's Encrypt or get trolled into oblivion. Trolls just repeat the same dumb talking points from some Mozilla blog. Apparently HTTP was deprecated back in 2015. Apparently HTTP is going away forever in a matter of months. Five years ago. Or something. Who wants to argue with trolls. Right?!

I figured I'd make notes. I'm gonna strap on some SSL to a plain HTTP server. I'm using php -S for HTTP and socat for SSL.

# apt-get install sudo

sudo apt-get update;sudo apt-get install php-cli socat

mkdir -v encrusted-ht.ml

echo '<html><head><title>Encrusted HTML</title></head><body><p>Hello Web</p></body></html>' > encrusted-ht.ml/index.html

( ( sudo -u nobody php -S 127.0.0.1:8080 -t encrusted-ht.ml ) & )

mkdir -v redirect

cat > redirect/redirect.php <<eof
<?php
if (preg_match('/^\/\./', \$_SERVER["REQUEST_URI"])) {
return false;
} else {
header("Location: https://" . \$_SERVER["HTTP_HOST"] . \$_SERVER["REQUEST_URI"], true, 301);
}
?>
eof

( ( sudo -u nobody php -S 0.0.0.0:8888 -t redirect redirect/redirect.php ) & )

sudo iptables -t nat -A PREROUTING -p tcp --syn --dport 80 -j REDIRECT --to-ports 8888

git clone https://github.com/acmesh-official/acme.sh acme.sh.git

mkdir -v .acme.sh;chmod -v 700 .acme.sh;cp -ipv acme.sh.git/acme.sh .acme.sh

.acme.sh/acme.sh --issue -d encrusted-ht.ml -d www.encrusted-ht.ml -w redirect

( ( socat -d -d openssl-listen:4444,cert=.acme.sh/encrusted-ht.ml/encrusted-ht.ml.cer,key=.acme.sh/encrusted-ht.ml/encrusted-ht.ml.key,cafile=.acme.sh/encrusted-ht.ml/ca.cer,verify=0,fork,keepalive,linger=60,nodelay,reuseaddr tcp:127.0.0.1:8080,keepalive,linger=60,nodelay ) & )

sudo iptables -t nat -A PREROUTING -p tcp --syn --dport 443 -j REDIRECT --to-ports 4444

When does something interesting happen???

Well that was pointless. Let's Encrypt issues me a certificate for existing. It's snake oil and I might as well sign my own certificate. But oh no self signing would be self-signed and that's bad. Because reasons. Let's Encrypt has to sign my certificate otherwise I'm not trustworthy enough. But Let's Encrypt is fully automated crap that trusts everyone. It's snake oil.

I'm convinced! Let's Encrypt is simply a social movement for trolls.

Sunday July 12, 20
03:45 AM
Code

Hi, hi, techies!!

HTML5 audio is the best thing since MP3. Not only can you play MP3 files in your web browser, but using JavaScript you can make an audio player in a web page with playlists and everything. If you want to play audio in a mobile browser on your phone, you can do that. Your phone's lock screen will show you what's playing too.

But if you want to advance a playlist with JavaScript in a mobile browser while your phone is locked and the screen is off, that's when you'll fall down a rabbit hole. Mobile browsers don't like to run JavaScript with the screen off, not even while the phone is plugged in. So if you want to lock your phone, turn off the screen, plug in a charger, and play music all night, mobile browsers are going to give you trouble.

First let's talk about putting that sweet metadata on the lock screen so you can see what's playing.

Android Chrome puts the document title on the lock screen by default. Sure you can set the document title but you can also do better. The Media Session API lets you put artist name, song title, and album art on the lock screen, and you can define functions for the skip back and skip forward buttons too. The only disadvantage is you have to arrange your metadata in an ugly JSON structure.

The Media Session API is the best thing since HTML5 audio. It puts a scriptable audio player on the lock screen, and it can control a playlist from the lock screen too.

iOS Safari puts the audio element title attribute on the lock screen. If the audio element doesn't have a title, the URL of the audio file is shown instead, and that's terrible. So you always want to put your metadata into the title attribute of your HTML5 audio element.

So, metadata on the lock screen is easy. Advancing a playlist is hard.

Second let's talk about keeping JavaScript running on a locked phone with the screen off.

If you're using HTML5 audio the obvious way to change what's playing when a track ends is to run some JavaScript to play the next track when the ended event fires. Sure is difficult to do that though if JavaScript stops running as soon as audio stops playing. Mobile browsers act like turning off your screen means you want to stop listening at the end of the current track. But we're talking about audio here. You don't need the screen on to listen to audio. What if you want audio to continue while the screen is off.

Well the trick to continuous audio is never let audio stop. Of course Android and iOS require different trickery.

Android Chrome will continue to run JavaScript with the screen off if you put a looping HTML5 audio element on the page. It can be hidden, it can be muted, but it must be playing. When your HTML5 audio player reaches track end, the ended event will fire, and you can run JavaScript to change the track. It doesn't matter that audio ended between tracks, because your hidden muted looping audio is still playing.

iOS Safari will continue to run JavaScript with the screen off if you play background audio with the Web Audio API. A silent tone generator made from an oscillator connected to a gain set to zero is enough. You don't even have to set anything to loop. The Web Audio API makes it easy to play audio forever.

So! I made a simple example: HTML5 audio for playlist, Media Session API for metadata, and Web Audio API for tone generator. I think it works. I think I distilled my methods to the essentials. I think I produced a solution that works for both Android and iOS. I tried Android Chrome 83 and iOS 13.5.1 Safari.

<html><head><title>Lock Screen Demo</title></head><body>
<script>
var urls = [
"//mp3-128.jango.com/music/00/28/26/0028266081.mp3",
"//ra-mp3-128.jango.com/music/b4b79848bd80367610ae7067727a0976.mp3",
"//ra-mp3-128.jango.com/music/41c1d16b60c3f465848bf119201bfb83.mp3"
];
var meta = [
{"title":"Jango",
"artist":"InspectorSunday",
"artwork":[{"sizes":"320x180",
"src":"//artist1.jango.com/8a6/8a60e8dbccff1083ba7b7b4292594c8f_xl.jpg"}]},
{"title":"JangoPhoneOpen",
"artist":"Jake Test",
"artwork":[{"sizes":"320x180",
"src":"//artist.radioairplay.com/airplay_pictures/pictures/000/180/814/jango-logo_2_300.png"}]},
{"title":"Jango voice mail closed",
"artist":"erin+ddaPT_TierFree@jango.com",
"artwork":[{"sizes":"320x180",
"src":"//artist.radioairplay.com/airplay_pictures/pictures/000/663/482/e14181a66c596378eb4c902c0a0eacaf_300.png"}]}
];
var position = 0;
var scheduletimeout = [undefined, 0];
var timeout;

function schedule () {
if (scheduletimeout[1]) {
if (new Date().getTime() > scheduletimeout[1]) {
scheduletimeout[0]();
scheduletimeout = [undefined, 0];
} } }

function next (direction, delay) {
document.getElementById("player").play();
document.getElementById("player").pause();
var schedulednext = function () {
scheduletimeout = [undefined, 0];
if (timeout) clearTimeout(timeout);
timeout = undefined;
document.getElementById("player").play();
document.getElementById("player").pause();
position += urls.length + direction;
position %= urls.length;
document.getElementById("source").src = urls[position];
document.getElementById("player").load();
document.getElementById("player").play();
};
scheduletimeout = [schedulednext, new Date().getTime() + delay];
if (timeout) clearTimeout(timeout);
timeout = setTimeout(schedulednext, delay);
}

var actionhandlerdone;

function show () {
var title = meta[position]["title"] + " - " + meta[position]["artist"] +
" : Lock Screen Demo";
document.title = title;
document.getElementById("player").title = title;
document.getElementById("mutedloop").title = title;
if (navigator.userAgent.match(/android/i)) {
document.getElementById("mutedloop").pause();
document.getElementById("mutedloop").play();
}
if ('mediaSession' in navigator) {
navigator.mediaSession.metadata = new MediaMetadata(meta[position]);
if (!actionhandlerdone) {
actionhandlerdone = true;
navigator.mediaSession.setActionHandler('seekbackward', function() {
document.getElementById("player").currentTime -= 7;
});
navigator.mediaSession.setActionHandler('seekforward', function() {
document.getElementById("player").currentTime += 7;
});
navigator.mediaSession.setActionHandler('previoustrack', function() {
next(-1, 0);
});
navigator.mediaSession.setActionHandler('nexttrack', function() {
next(1, 0);
});
} } }

var webaudiodone;
var audioCtx;
var gainNode;
var oscillator;

function webaudio () {
if (!webaudiodone) {
webaudiodone = true;
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
gainNode = audioCtx.createGain();
oscillator = audioCtx.createOscillator();
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
gainNode.gain.setValueAtTime(0, audioCtx.currentTime);
oscillator.start();
} }

function button () {
document.getElementById("play").style.display = "none";
document.getElementById("player").style.display = "";
if (navigator.userAgent.match(/cpu.*os 1[0-3]_[0-9].*applewebkit.*mobile/i)) {
document.getElementById("mutedloop").muted = false;
document.getElementById("mutedloop").play();
document.getElementById("mutedloop").pause();
webaudio();
document.getElementById("player").play();
if (navigator.userAgent.match(/cpu.*os 12_[2-9].*applewebkit.*mobile/i)) {
document.getElementById("mutedloop").muted = true;
} } else {
if (navigator.userAgent.match(/android/i)) {
show();
document.getElementById("mutedloop").play();
}
document.getElementById("player").play();
} }
</script>
<button id="play" onclick="button()"
style="height:100px;width:100px;border:none;
font-size:24px;background-color:black;color:white">Play</button>
<audio id="player" playsinline controls preload="metadata"
style="display:none"
onplay="show()" onended="next(1, 15000)">
<source id="source"
src="//mp3-128.jango.com/music/00/28/26/0028266081.mp3"></audio>
<audio id="mutedloop" muted loop preload="metadata"
style="display:none"
ontimeupdate="schedule()"
src="//mp3-128.jango.com/music/00/28/26/0028266081.mp3"></audio>
</body></html>

Live demo at http://jango-index.ml/lockscreen.html
Press play, lock phone.

I've been down this rabbit hole for many years. Mobile browsers suck.

Update: added compatibility for iOS 12 and Android Firefox. iOS 12.2 is the worst.

Monday July 06, 20
12:06 AM
News

Found in a letter to the Elko Daily Free Press (Elko, Nevada):

Letter: It's a face mask, not a neck mask

Jun 30, 2020

Editor:

Yesterday I visited two local businesses. The first had four women working and all had face masks on around their neck, not on their face. The second business had three women working and not a face mask in sight.

Maybe I am wrong but I thought we all had to wear a mask in public or maybe some people are above that.

Steve Hulet

Spring Creek

I wear a face mask in public, on my face. I see people wearing masks around their necks, and I don't appreciate this disturbingly common practice. If they're not going to be serious about wearing masks, I'd rather see people not wearing masks at all. Neck mask is not an effective compromise.

Sunday June 21, 20
03:52 AM
Code

Hi, hi, techies!!

Jango. You've never heard of it because it's not Pandora. The description on jango.com actually says "Free internet radio, just like Pandora." Oh yeah. Jango is overshadowed by Pandora, so much.

Yeah yeah it's like Pandora except Jango has a ridiculously simple API and you could spend a few months hitting a couple of API endpoints a few million times and have a complete collection of metadata about every song on Jango. You don't even need an account and you could do it as a guest. So that's what I've been doing for the past few months.

The relevant API endpoints are these:

https://www.jango.com/stations/1/tunein?song_id=

The song_id is sequential between 1 and 2558256 and counting. Jango receives uploads of about five songs per hour.

https://www.jango.com/streams/info

A JSON report about what's playing. If you tuned a station to a song_id then you usually get JSON about that song_id unless an error occurs.

Why bother? Well I really really like Jango but I hate the web site and I hate the mobile apps and I hate the search page. So I'm making my own index of Jango so I can search it the way I want.

http://jango-index.ml/

And here's all the source code:

http://jango-index.ml/src/

It's PHP and Bash and it uses SQLite. Not documented in the source is how I use BTRFS compressed by ZSTD because I store millions of JSON files in one directory.

Nobody uses Jango, why journal about it? Well there really isn't any appropriate forum to discuss the Jango API. I sometimes find projects that use the Jango API but nothing at this scale. So I figured I'd mention it somewhere. I wanted to share the awesomeness of Jango and and maybe another someone will explore the Jango API too.

Have fun breaking my shht, techies!!