Minamp 1.1

- Run network/TLS work on 256 KB-stack threads (net/Threads.java): Android 2.x's
  ~16 KB default stack overflowed during the bundled TLS handshake
- Close pooled TLS connections off the caller thread
- Try several Plex music-transcoder URL variants until the server accepts one,
  and remember the one that worked
- Accept pasted server URLs with paths; default to 443 for https
- Catch fatal errors and save a crash report (shown on next launch and written
  to Minamp-error.txt on the SD card)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xockoandClaude Opus 5.5 committed 2026-10-14 19:59:58 +02:00
1 parent 948d1198ad
commit 0a2ac901bd
16 files changed
+417 -80

No files matched your search

+52 -11
View File
@@ -36,7 +36,7 @@ public final class Library {
}
public String recentlyPlayedPath(int size) {
return "/library/sections/" + section + "/all?type=9&sort=lastViewedAt:desc&viewCount>>=0" + page(0, size);
return "/library/sections/" + section + "/all?type=9&sort=lastViewedAt:desc&viewCount%3E%3E=0" + page(0, size);
}
public String playlistsPath() {
@@ -84,7 +84,7 @@ public final class Library {
/** Tracks rated four stars or better (and loved tracks), shuffled. */
public List<Item> topRatedRadio() throws IOException {
return server.items("/library/sections/" + section + "/all?type=10&userRating>>=7&sort=random" + page(0, 100));
return server.items("/library/sections/" + section + "/all?type=10&userRating%3E%3E=7&sort=random" + page(0, 100));
}
/** Tracks from albums released in a random decade that the library actually has. */
@@ -172,15 +172,56 @@ public final class Library {
return server.url(t.partKey);
}
/** MP3 transcode over plain progressive HTTP, starting at offsetMs. */
public String transcodeUrl(Item t, int kbps, long offsetMs, String session) throws IOException {
/**
* Plex servers differ in which form of the music transcoder request they accept (some answer
* 400 to one and stream fine with another), so there are several known variants. The proxy
* tries them in order; the one that worked is remembered and tried first next time.
*/
public static final int VARIANTS = 4;
public static volatile int preferredVariant = 0;
public static int[] variantOrder() {
int[] order = new int[VARIANTS];
order[0] = preferredVariant;
for (int i = 0, k = 1; i < VARIANTS; i++) if (i != preferredVariant) order[k++] = i;
return order;
}
/** MP3 transcode URLs over plain progressive HTTP starting at offsetMs, in {@link #variantOrder()} order. */
public java.util.List<String> transcodeUrls(Item t, int kbps, long offsetMs, String session) throws IOException {
int[] order = variantOrder();
java.util.List<String> out = new ArrayList<String>();
for (int i = 0; i < order.length; i++) out.add(transcodeUrl(t, kbps, offsetMs, session, order[i]));
return out;
}
String transcodeUrl(Item t, int kbps, long offsetMs, String session, int variant) throws IOException {
if (kbps <= 0) kbps = 320;
String profile = "add-transcode-target(type=musicProfile&context=streaming&protocol=http&container=mp3&audioCodec=mp3)"
+ "+add-limitation(scope=musicCodec&scopeName=mp3&type=upperBound&name=audio.bitrate&value=" + kbps + ")"
+ "+add-limitation(scope=musicCodec&scopeName=mp3&type=upperBound&name=audio.channels&value=2)";
String path = "/music/:/transcode/universal/start.mp3"
String target = "add-transcode-target(type=musicProfile&context=streaming&protocol=http&container=mp3&audioCodec=mp3)";
String profile;
String platform;
String base = "/music/:/transcode/universal/start.mp3";
switch (variant) {
case 0: // what Plex's own web player identifies as, with an explicit MP3 target
platform = "Chrome";
profile = target + "+add-limitation(scope=musicCodec&scopeName=*&type=upperBound&name=audio.bitrate&value=" + kbps + ")";
break;
case 1: // generic client profile
platform = "Generic";
profile = target + "+add-limitation(scope=musicCodec&scopeName=mp3&type=upperBound&name=audio.bitrate&value=" + kbps + ")";
break;
case 2: // older servers' audio endpoint
platform = "Chrome";
profile = target;
base = "/audio/:/transcode/universal/start.mp3";
break;
default: // no profile tweaks at all; bitrate from musicBitrate only
platform = "Chrome";
profile = null;
}
String path = base
+ "?path=" + Plex.enc("/library/metadata/" + t.ratingKey)
+ "&mediaIndex=0&partIndex=0&protocol=http&hasMDE=1&fastSeek=1"
+ "&mediaIndex=0&partIndex=0&protocol=http&hasMDE=1&fastSeek=1&copyts=1"
+ "&directPlay=0&directStream=0&directStreamAudio=0"
+ "&musicBitrate=" + kbps + "&maxAudioBitrate=" + kbps
+ "&offset=" + (offsetMs / 1000)
@@ -190,9 +231,9 @@ public final class Library {
+ "&X-Plex-Client-Identifier=" + Plex.enc(Plex.clientId)
+ "&X-Plex-Product=" + Plex.enc(Plex.product)
+ "&X-Plex-Version=" + Plex.enc(Plex.version)
+ "&X-Plex-Platform=Generic"
+ "&X-Plex-Platform=" + platform
+ "&X-Plex-Device=" + Plex.enc(Plex.device)
+ "&X-Plex-Client-Profile-Extra=" + Plex.enc(profile);
+ (profile != null ? "&X-Plex-Client-Profile-Extra=" + Plex.enc(profile) : "");
return server.url(path);
}