Task 560: .PLS File Format
Task 560: .PLS File Format
File Format Specifications for .PLS
The .PLS file format is a text-based, INI-style format used for multimedia playlists, primarily for audio streaming or local playback in media players like Winamp or VLC. It is case-sensitive and consists of a header, track entries, and a footer. The format supports URLs for streams or file paths for local media, with optional metadata.
1. List of All Properties Intrinsic to the File Format
The .PLS format is structured as key-value pairs under a [playlist] section. The properties (keys) are:
- Version: Specifies the format version (must be
2for validity; this is the only supported version). - NumberOfEntries: An integer indicating the total number of tracks (must match the highest entry index used).
- FileX (where X is an integer starting from 1): The URL or file path to the media file (required for each entry; e.g.,
File1=https://example.com/stream.mp3orFile1=/path/to/local.mp3). - TitleX (optional): The title of the track (e.g.,
Title1=Example Song). - LengthX (optional): The length of the track in seconds (e.g.,
Length1=120); use-1for indefinite lengths like streams.
These properties are intrinsic as they define the file's structure and content parsing rules. The file must start with [playlist] (case-sensitive), and keys are numbered sequentially without gaps. Whitespace around equals signs is allowed, but the format is generally forgiving of minor variations as long as the structure is maintained.
2. Two Direct Download Links for .PLS Files
Here are two direct links to .PLS playlist files (these are streaming radio playlists):
- http://s11.myradiostream.com:7622/listen.pls?sid=2
- http://streamingcwsradio10.com:9348/listen.pls?sid=1
3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .PLS File Dump
This is a self-contained HTML page with embedded JavaScript that can be embedded in a Ghost blog (or any HTML-supporting platform). It allows users to drag and drop a .PLS file, parses it, and dumps all properties (Version, NumberOfEntries, and per-entry File/Title/Length) to the screen.
Drag and Drop .PLS File
4. Python Class for .PLS Handling
This Python class can open, decode (read/parse), write, and print all properties to console.
import re
class PLSHandler:
def __init__(self, filepath=None):
self.version = 2
self.number_of_entries = 0
self.entries = [] # List of dicts: {'file': str, 'title': str or None, 'length': int or None}
if filepath:
self.read(filepath)
def read(self, filepath):
with open(filepath, 'r') as f:
content = f.read().strip()
lines = content.split('\n')
if lines[0].strip().lower() != '[playlist]':
raise ValueError("Invalid PLS file")
self.entries = []
entry_dict = {}
for line in lines[1:]:
line = line.strip()
if '=' in line:
key, value = line.split('=', 1)
key = key.strip()
value = value.strip()
if key.lower() == 'version':
self.version = int(value)
elif key.lower() == 'numberofentries':
self.number_of_entries = int(value)
else:
match = re.match(r'(\w+)(\d+)', key, re.IGNORECASE)
if match:
prop_type = match.group(1).lower()
index = int(match.group(2))
while len(entry_dict) < index:
entry_dict[len(entry_dict) + 1] = {}
if prop_type == 'file':
entry_dict[index]['file'] = value
elif prop_type == 'title':
entry_dict[index]['title'] = value
elif prop_type == 'length':
entry_dict[index]['length'] = int(value)
self.entries = [entry_dict[i] for i in sorted(entry_dict)]
if len(self.entries) != self.number_of_entries:
raise ValueError("Entry count mismatch")
def write(self, filepath):
with open(filepath, 'w') as f:
f.write('[playlist]\n')
for i, entry in enumerate(self.entries, 1):
if 'file' in entry:
f.write(f'File{i}={entry["file"]}\n')
if 'title' in entry:
f.write(f'Title{i}={entry["title"]}\n')
if 'length' in entry:
f.write(f'Length{i}={entry["length"]}\n')
f.write(f'NumberOfEntries={self.number_of_entries}\n')
f.write(f'Version={self.version}\n')
def print_properties(self):
print(f'Version: {self.version}')
print(f'NumberOfEntries: {self.number_of_entries}')
for i, entry in enumerate(self.entries, 1):
print(f'Entry {i}:')
print(f' File: {entry.get("file")}')
print(f' Title: {entry.get("title")}')
print(f' Length: {entry.get("length")}')
# Example usage:
# handler = PLSHandler('example.pls')
# handler.print_properties()
# handler.write('new.pls')
5. Java Class for .PLS Handling
This Java class can open, decode (read/parse), write, and print all properties to console.
import java.io.*;
import java.util.*;
import java.util.regex.*;
public class PLSHandler {
private int version = 2;
private int numberOfEntries = 0;
private List<Map<String, Object>> entries = new ArrayList<>(); // Each map: "file":String, "title":String, "length":Integer
public PLSHandler(String filepath) throws IOException {
if (filepath != null) {
read(filepath);
}
}
public void read(String filepath) throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(filepath))) {
String line = reader.readLine().trim();
if (!line.equalsIgnoreCase("[playlist]")) {
throw new IllegalArgumentException("Invalid PLS file");
}
Map<Integer, Map<String, Object>> entryMap = new TreeMap<>();
Pattern pattern = Pattern.compile("(\\w+)(\\d+)?=(.*)");
while ((line = reader.readLine()) != null) {
line = line.trim();
if (line.contains("=")) {
Matcher matcher = pattern.matcher(line);
if (matcher.matches()) {
String key = matcher.group(1).toLowerCase();
String value = matcher.group(3).trim();
if (key.equals("version")) {
version = Integer.parseInt(value);
} else if (key.equals("numberofentries")) {
numberOfEntries = Integer.parseInt(value);
} else {
int index = Integer.parseInt(matcher.group(2));
entryMap.computeIfAbsent(index, k -> new HashMap<>());
if (key.equals("file")) {
entryMap.get(index).put("file", value);
} else if (key.equals("title")) {
entryMap.get(index).put("title", value);
} else if (key.equals("length")) {
entryMap.get(index).put("length", Integer.parseInt(value));
}
}
}
}
}
entries = new ArrayList<>(entryMap.values());
if (entries.size() != numberOfEntries) {
throw new IllegalArgumentException("Entry count mismatch");
}
}
}
public void write(String filepath) throws IOException {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filepath))) {
writer.write("[playlist]\n");
for (int i = 0; i < entries.size(); i++) {
Map<String, Object> entry = entries.get(i);
if (entry.containsKey("file")) {
writer.write("File" + (i + 1) + "=" + entry.get("file") + "\n");
}
if (entry.containsKey("title")) {
writer.write("Title" + (i + 1) + "=" + entry.get("title") + "\n");
}
if (entry.containsKey("length")) {
writer.write("Length" + (i + 1) + "=" + entry.get("length") + "\n");
}
}
writer.write("NumberOfEntries=" + numberOfEntries + "\n");
writer.write("Version=" + version + "\n");
}
}
public void printProperties() {
System.out.println("Version: " + version);
System.out.println("NumberOfEntries: " + numberOfEntries);
for (int i = 0; i < entries.size(); i++) {
Map<String, Object> entry = entries.get(i);
System.out.println("Entry " + (i + 1) + ":");
System.out.println(" File: " + entry.get("file"));
System.out.println(" Title: " + entry.get("title"));
System.out.println(" Length: " + entry.get("length"));
}
}
// Example usage:
// public static void main(String[] args) throws IOException {
// PLSHandler handler = new PLSHandler("example.pls");
// handler.printProperties();
// handler.write("new.pls");
// }
}
6. JavaScript Class for .PLS Handling
This JavaScript class (for Node.js) can open, decode (read/parse), write, and print all properties to console. Requires fs module.
const fs = require('fs');
class PLSHandler {
constructor(filepath = null) {
this.version = 2;
this.numberOfEntries = 0;
this.entries = []; // Array of objects: {file: string, title: string|null, length: number|null}
if (filepath) {
this.read(filepath);
}
}
read(filepath) {
const content = fs.readFileSync(filepath, 'utf-8').trim();
const lines = content.split('\n');
if (lines[0].trim().toLowerCase() !== '[playlist]') {
throw new Error('Invalid PLS file');
}
const entryDict = {};
const regex = /^(\w+)=(.+)$/;
lines.slice(1).forEach(line => {
line = line.trim();
const match = line.match(regex);
if (match) {
const key = match[1];
const value = match[2].trim();
if (key.toLowerCase() === 'version') {
this.version = parseInt(value);
} else if (key.toLowerCase() === 'numberofentries') {
this.numberOfEntries = parseInt(value);
} else {
const entryMatch = key.match(/^(\w+)(\d+)$/i);
if (entryMatch) {
const type = entryMatch[1].toLowerCase();
const index = parseInt(entryMatch[2]);
if (!entryDict[index]) entryDict[index] = {};
if (type === 'file') entryDict[index].file = value;
if (type === 'title') entryDict[index].title = value;
if (type === 'length') entryDict[index].length = parseInt(value);
}
}
}
});
this.entries = Object.values(entryDict).sort((a, b) => a.index - b.index);
if (this.entries.length !== this.numberOfEntries) {
throw new Error('Entry count mismatch');
}
}
write(filepath) {
let data = '[playlist]\n';
this.entries.forEach((entry, index) => {
const i = index + 1;
if (entry.file) data += `File${i}=${entry.file}\n`;
if (entry.title) data += `Title${i}=${entry.title}\n`;
if (entry.length !== undefined) data += `Length${i}=${entry.length}\n`;
});
data += `NumberOfEntries=${this.numberOfEntries}\n`;
data += `Version=${this.version}\n`;
fs.writeFileSync(filepath, data);
}
printProperties() {
console.log(`Version: ${this.version}`);
console.log(`NumberOfEntries: ${this.numberOfEntries}`);
this.entries.forEach((entry, index) => {
console.log(`Entry ${index + 1}:`);
console.log(` File: ${entry.file}`);
console.log(` Title: ${entry.title}`);
console.log(` Length: ${entry.length}`);
});
}
}
// Example usage:
// const handler = new PLSHandler('example.pls');
// handler.printProperties();
// handler.write('new.pls');
7. C++ Class for .PLS Handling
This C++ class can open, decode (read/parse), write, and print all properties to console. Uses standard libraries.
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <regex>
#include <string>
struct Entry {
std::string file;
std::string title;
int length = -2; // -2 means not set
};
class PLSHandler {
private:
int version = 2;
int numberOfEntries = 0;
std::vector<Entry> entries;
public:
PLSHandler(const std::string& filepath = "") {
if (!filepath.empty()) {
read(filepath);
}
}
void read(const std::string& filepath) {
std::ifstream file(filepath);
if (!file.is_open()) {
throw std::runtime_error("Could not open file");
}
std::string line;
std::getline(file, line);
if (line.find("[playlist]") == std::string::npos) {
throw std::runtime_error("Invalid PLS file");
}
std::map<int, Entry> entryMap;
std::regex regex(R"(^(\w+)=(.+)$)");
while (std::getline(file, line)) {
std::smatch match;
if (std::regex_match(line, match, regex)) {
std::string key = match[1];
std::string value = match[2];
if (key == "Version") {
version = std::stoi(value);
} else if (key == "NumberOfEntries") {
numberOfEntries = std::stoi(value);
} else {
std::regex entryRegex(R"(^(\w+)(\d+)$)");
std::smatch entryMatch;
if (std::regex_match(key, entryMatch, entryRegex)) {
std::string type = entryMatch[1];
int index = std::stoi(entryMatch[2]);
if (type == "File") {
entryMap[index].file = value;
} else if (type == "Title") {
entryMap[index].title = value;
} else if (type == "Length") {
entryMap[index].length = std::stoi(value);
}
}
}
}
}
file.close();
for (auto& pair : entryMap) {
entries.push_back(pair.second);
}
if (entries.size() != static_cast<size_t>(numberOfEntries)) {
throw std::runtime_error("Entry count mismatch");
}
}
void write(const std::string& filepath) {
std::ofstream file(filepath);
if (!file.is_open()) {
throw std::runtime_error("Could not open file for writing");
}
file << "[playlist]\n";
for (size_t i = 0; i < entries.size(); ++i) {
size_t idx = i + 1;
if (!entries[i].file.empty()) {
file << "File" << idx << "=" << entries[i].file << "\n";
}
if (!entries[i].title.empty()) {
file << "Title" << idx << "=" << entries[i].title << "\n";
}
if (entries[i].length != -2) {
file << "Length" << idx << "=" << entries[i].length << "\n";
}
}
file << "NumberOfEntries=" << numberOfEntries << "\n";
file << "Version=" << version << "\n";
file.close();
}
void printProperties() {
std::cout << "Version: " << version << std::endl;
std::cout << "NumberOfEntries: " << numberOfEntries << std::endl;
for (size_t i = 0; i < entries.size(); ++i) {
std::cout << "Entry " << (i + 1) << ":" << std::endl;
std::cout << " File: " << entries[i].file << std::endl;
std::cout << " Title: " << entries[i].title << std::endl;
std::cout << " Length: " << entries[i].length << std::endl;
}
}
};
// Example usage:
// int main() {
// try {
// PLSHandler handler("example.pls");
// handler.printProperties();
// handler.write("new.pls");
// } catch (const std::exception& e) {
// std::cerr << e.what() << std::endl;
// }
// return 0;
// }