Skip to content

Commit 93e17ff

Browse files
committed
- adding -lt/--limit to the General options, currently being used
by the m3u to CSV converter - adding m3u.md (and m3u.html) - updating mpv windows download link
1 parent 527e8d8 commit 93e17ff

File tree

6 files changed

+321
-9
lines changed

6 files changed

+321
-9
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ If you are a packager and would like to produce a package for your distribution
149149

150150
3. [Vifm](https://vifm.info/) - A file manager with curses interface, which provides a Vi[m]-like environment.
151151

152+
4. [pyradio-m3u-to-playlist](https://github.com/LionyxML/pyradio-m3u-to-playlist) by [LionyxML](https://github.com/LionyxML) - An M3U to **PyRadio** CSV converter which inspired the M3U fumctionality.
153+
152154
## Special thanks
153155

154156
1. [edunfelt](https://github.com/edunfelt), for her wonderful work on [base16 themes](https://github.com/edunfelt/base16-pyradio), and ideas regarding theming and such.

devel/pre-commit

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,8 @@ for afile in index.md \
157157
headless.md \
158158
client.md \
159159
rec-dir.md \
160-
buffering.md
160+
buffering.md \
161+
m3u.md
161162
do
162163
sed -i -e "s/'s-/s-/g" -e "s/s'-/s-/g" "$afile"
163164
git add "$afile"
@@ -202,6 +203,8 @@ do
202203
echo '% PyRadio Recordings Directory' > tmp.md
203204
elif [ "$afile" = "buffering.md" ];then
204205
echo '% PyRadio Buffering' > tmp.md
206+
elif [ "$afile" = "m3u.md" ];then
207+
echo '% PyRadio M3U Playlist Support' > tmp.md
205208
fi
206209
cat ${afile} >> tmp.md
207210
pandoc --wrap=none -s -t html tmp.md -o ${out} || {

docs/m3u.html

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
<!DOCTYPE html>
2+
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="generator" content="pandoc" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
7+
<title>PyRadio M3U Playlist Support</title>
8+
<style>
9+
body {padding: 2em;}
10+
@media screen and (max-width: 770px) {
11+
body {padding: 10px;}
12+
}
13+
body {max-width: 750px; margin: auto;}
14+
h2 {margin-top: 2.5em; border-bottom:1px solid SaddleBrown; color: SaddleBrown;}
15+
h3 {margin-top: 2em; color: SaddleBrown; text-decoration: underline SaddleBrown}
16+
h4 {margin: 2em 0 1em 0; color: SaddleBrown; font-size: 1em;}
17+
h4:before {content: "# "; font-weight: bold; vertical-align: middle;}
18+
h5 {margin: 2em 0 1em 0; color: SaddleBrown; font-size: 1em;;}
19+
h5:before {content: "## "; font-weight: bold; vertical-align: middle;}
20+
p, table, ol, ul {margin-left: .8em;}
21+
STRONG {color: SaddleBrown;}
22+
dl {margin: 2em;}
23+
dd {margin: 1em;}
24+
dt {font-weight: bold;}
25+
TABLE {border: 1px solid SaddleBrown; border-collapse: collapse; margin-left: auto; margin-right: auto; border-radius: 5px; -moz-border-radius: 5px; border-collapse:separate; box-shadow: 5px 5px 15px #888888;}
26+
TH {text-align: left; vertical-align: top; padding: 5px;color: SaddleBrown;border: 1px solid SaddleBrown; background-color: SaddleBrown; color: white;}
27+
TD {text-align: left; vertical-align: top; padding: 5px 10px;border: 1px solid SaddleBrown;}
28+
pre { background-color: rgba(245, 245, 245, 1); color: #474747; padding: 1.5em; border: 1px solid #C7C7C7; border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px; overflow: auto; box-shadow: 5px 5px 15px #C7C7C7;}
29+
.task-list {list-style-type: none; padding: 0; margin: 0 0 0 1em ;}
30+
img{display: block; margin-left: auto; margin-right: auto; max-width: 750px; width: 100%; background:transparent; padding:3px; border:1px solid #999999; border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px; box-shadow:5px 5px 15px #888888;}
31+
.indented {text-indent: -1.5em; padding-left: 1.5em; margin-left: 1em;}
32+
a{ color: SaddleBrown;}
33+
a:visited{color: SaddleBrown;}
34+
</style>
35+
</head>
36+
<body>
37+
<header id="title-block-header">
38+
</header>
39+
<h1 style="color: SaddleBrown" id="pyradio-m3u-playlist-support">PyRadio M3U Playlist Support</h1>
40+
<p><strong>PyRadio</strong> offers seamless two-way conversion between its native CSV playlist format and the widely supported M3U format. This functionality allows you to import radio stations from external M3U sources and export your <strong>PyRadio</strong> stations to share with other media players.</p>
41+
<h2 id="what-you-can-do">What You Can Do <span style="padding-left: 10px;"><sup style="font-size: 50%"><a href="#" title="Go to top of the page">Top</a></sup></span></h2>
42+
<h3 id="convert-m3u-to-pyradio-csv">Convert M3U to PyRadio CSV</h3>
43+
<p>When you have an M3U playlist file (from another media player or online source), <strong>PyRadio</strong> can convert it to its CSV format while preserving important metadata like station logos and group categories. The conversion process:</p>
44+
<ul>
45+
<li>Reads standard M3U files (both local and remote URLs)</li>
46+
<li>Extracts station names, URLs, and metadata</li>
47+
<li>Preserves group/category information</li>
48+
<li>Validates URLs to ensure they’re working streams</li>
49+
<li>Handles various character encodings automatically</li>
50+
<li>Converts HTML entities to readable text</li>
51+
</ul>
52+
<p>The default maximum number of stations in an M3U file is 10,000.</p>
53+
<p>You can perform M3U to CSV conversion in two ways:</p>
54+
<ol type="1">
55+
<li><p><strong>Command line conversion</strong> using the --convert option.<br />
56+
<br />
57+
This is ideal for batch conversions of multiple files (or M3U URLs).<br />
58+
<br />
59+
It also provides a way to convert very large M3Us to CSV; the “-lm” / “--limit” parameter will help you overcome the default 10,000 stations limit.</p></li>
60+
<li><p><strong>Automatic conversion within PyRadio</strong> when selecting M3U playlists from the playlist browser<br />
61+
<br />
62+
<strong>PyRadio</strong> automatically detects M3U files in your playlists directory and labels them with ” (m3u)” suffix. When you select an M3U playlist from within <strong>PyRadio</strong>, it transparently converts it to CSV format for editing and use within the application.<br />
63+
<br />
64+
If both M3U and CSV versions of a playlist exist, <strong>PyRadio</strong> prioritizes the CSV file, assuming it’s your preferred edited version.</p></li>
65+
</ol>
66+
<h3 id="convert-pyradio-csv-to-m3u">Convert PyRadio CSV to M3U</h3>
67+
<p>When you want to use your <strong>PyRadio</strong> stations with other media players or share them with others, you can export to the universal M3U format. The conversion:</p>
68+
<ul>
69+
<li>Maintains all your station information</li>
70+
<li>Preserves PyRadio-specific settings through custom tags</li>
71+
<li>Formats the output for maximum compatibility</li>
72+
<li>Applies proper character escaping for M3U standards</li>
73+
<li>Includes both standard and extended M3U metadata</li>
74+
</ul>
75+
<h2 id="character-conversions-in-csvm3u-conversion">Character Conversions in CSV→M3U Conversion <span style="padding-left: 10px;"><sup style="font-size: 50%"><a href="#" title="Go to top of the page">Top</a></sup></span></h2>
76+
<p>When converting from <strong>PyRadio</strong> CSV to M3U format, certain characters are automatically converted for better compatibility and visual presentation:</p>
77+
<table>
78+
<colgroup>
79+
<col style="width: 45%" />
80+
<col style="width: 33%" />
81+
<col style="width: 21%" />
82+
</colgroup>
83+
<thead>
84+
<tr>
85+
<th>Original Character</th>
86+
<th>Converted To</th>
87+
<th>Purpose</th>
88+
</tr>
89+
</thead>
90+
<tbody>
91+
<tr>
92+
<td>Comma (,)</td>
93+
<td>Middle dot (·) with spaces</td>
94+
<td>Prevents parsing issues in M3U files</td>
95+
</tr>
96+
<tr>
97+
<td>Hyphen (-)</td>
98+
<td>En dash (–) with spaces</td>
99+
<td>Improved visual appearance</td>
100+
</tr>
101+
<tr>
102+
<td>Straight quote (")</td>
103+
<td>Right double quotation mark (”)</td>
104+
<td>Better typography</td>
105+
</tr>
106+
</tbody>
107+
</table>
108+
<p>These conversions help maintain data integrity while ensuring the M3U files work reliably across different media players.</p>
109+
<h2 id="field-correspondence">Field Correspondence <span style="padding-left: 10px;"><sup style="font-size: 50%"><a href="#" title="Go to top of the page">Top</a></sup></span></h2>
110+
<p>Here’s how PyRadio’s internal fields map to M3U attributes:</p>
111+
<table>
112+
<colgroup>
113+
<col style="width: 34%" />
114+
<col style="width: 36%" />
115+
<col style="width: 29%" />
116+
</colgroup>
117+
<thead>
118+
<tr>
119+
<th>PyRadio Field</th>
120+
<th>M3U Equivalent</th>
121+
<th>Description</th>
122+
</tr>
123+
</thead>
124+
<tbody>
125+
<tr>
126+
<td>Station.name</td>
127+
<td>#EXTINF title</td>
128+
<td>The display name of the radio station</td>
129+
</tr>
130+
<tr>
131+
<td>Station.url</td>
132+
<td>Direct URL line</td>
133+
<td>The stream URL (after #EXTINF line)</td>
134+
</tr>
135+
<tr>
136+
<td>Station.encoding</td>
137+
<td>#PYRADIO-ENCODING</td>
138+
<td>Character encoding specification</td>
139+
</tr>
140+
<tr>
141+
<td>Station.icon</td>
142+
<td>tvg-logo attribute or #EXTIMG</td>
143+
<td>Station icon image URL</td>
144+
</tr>
145+
<tr>
146+
<td>Station.profile</td>
147+
<td>#PYRADIO-PROFILE</td>
148+
<td>Audio profile setting</td>
149+
</tr>
150+
<tr>
151+
<td>Station.buffering</td>
152+
<td>network-caching + #PYRADIO-BITRATE</td>
153+
<td>Buffer and bitrate settings</td>
154+
</tr>
155+
<tr>
156+
<td>Station.http</td>
157+
<td>#PYRADIO-HTTP</td>
158+
<td>Force HTTP option (special PyRadio feature)</td>
159+
</tr>
160+
<tr>
161+
<td>Station.volume</td>
162+
<td>#PYRADIO-VOLUME</td>
163+
<td>Volume preset for the station</td>
164+
</tr>
165+
<tr>
166+
<td>Station.referer</td>
167+
<td>http-referrer option</td>
168+
<td>HTTP referer header for the stream</td>
169+
</tr>
170+
<tr>
171+
<td>Station.player</td>
172+
<td>#PYRADIO-PLAYER</td>
173+
<td>Preferred media player for this station</td>
174+
</tr>
175+
</tbody>
176+
</table>
177+
<p>Group information is preserved through M3U’s group-title attribute and/or #EXTGRP directives.</p>
178+
<p>All <strong>#PYRADIO-</strong>* fields inserted in created M3Us are there to preserve <strong>PyRadio</strong> specific data; they will be ignored by all existing players but will be used if the M3U is converted back to a CSV.</p>
179+
<h2 id="command-line-usage">Command Line Usage <span style="padding-left: 10px;"><sup style="font-size: 50%"><a href="#" title="Go to top of the page">Top</a></sup></span></h2>
180+
<p>The conversion functionality is accessible through PyRadio’s command line interface:</p>
181+
<div class="sourceCode" id="cb1"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Convert M3U to CSV</span></span>
182+
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="ex">pyradio</span> <span class="at">--convert</span> playlist.m3u</span>
183+
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a></span>
184+
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="co"># Convert online M3U to CSV</span></span>
185+
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="ex">pyradio</span> <span class="at">--convert</span> https://radio-site.org/m3u/playlist.m3u</span>
186+
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a></span>
187+
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="co"># Convert CSV to M3U</span></span>
188+
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="ex">pyradio</span> <span class="at">--convert</span> radio.csv</span>
189+
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a></span>
190+
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="co"># Specify output file for conversion</span></span>
191+
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="ex">pyradio</span> <span class="at">--convert</span> radio <span class="at">-o</span> my_playlist.m3u</span>
192+
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a></span>
193+
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a><span class="co"># Force overwrite without confirmation</span></span>
194+
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="ex">pyradio</span> <span class="at">--convert</span> radio <span class="at">-o</span> my_playlist <span class="at">-y</span></span>
195+
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a></span>
196+
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a><span class="co"># Limit the number of processed stations</span></span>
197+
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a><span class="co"># This one actually extends it from 10,000 to 50,000</span></span>
198+
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a><span class="ex">pyradio</span> <span class="at">--convert</span> large_playlist.m3u <span class="at">-lm</span> 50000</span></pre></div>
199+
</body>
200+
</html>

docs/m3u.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# PyRadio M3U Playlist Support
2+
3+
**PyRadio** offers seamless two-way conversion between its native CSV playlist format and the widely supported M3U format. This functionality allows you to import radio stations from external M3U sources and export your **PyRadio** stations to share with other media players.
4+
5+
## What You Can Do
6+
7+
### Convert M3U to PyRadio CSV
8+
When you have an M3U playlist file (from another media player or online source), **PyRadio** can convert it to its CSV format while preserving important metadata like station logos and group categories. The conversion process:
9+
10+
- Reads standard M3U files (both local and remote URLs)
11+
- Extracts station names, URLs, and metadata
12+
- Preserves group/category information
13+
- Validates URLs to ensure they're working streams
14+
- Handles various character encodings automatically
15+
- Converts HTML entities to readable text
16+
17+
The default maximum number of stations in an M3U file is 10,000.
18+
19+
You can perform M3U to CSV conversion in two ways:
20+
21+
1. **Command line conversion** using the `--convert` option. \
22+
\
23+
This is ideal for batch conversions of multiple files (or M3U URLs). \
24+
\
25+
It also provides a way to convert very large M3Us to CSV; the "`-lm`" / "`--limit`" parameter will help you overcome the default 10,000 stations limit.
26+
27+
2. **Automatic conversion within PyRadio** when selecting M3U playlists from the playlist browser \
28+
\
29+
**PyRadio** automatically detects M3U files in your playlists directory and labels them with " (m3u)" suffix. When you select an M3U playlist from within **PyRadio**, it transparently converts it to CSV format for editing and use within the application. \
30+
\
31+
If both M3U and CSV versions of a playlist exist, **PyRadio** prioritizes the CSV file, assuming it's your preferred edited version.
32+
33+
### Convert PyRadio CSV to M3U
34+
When you want to use your **PyRadio** stations with other media players or share them with others, you can export to the universal M3U format. The conversion:
35+
36+
- Maintains all your station information
37+
- Preserves PyRadio-specific settings through custom tags
38+
- Formats the output for maximum compatibility
39+
- Applies proper character escaping for M3U standards
40+
- Includes both standard and extended M3U metadata
41+
42+
## Character Conversions in CSV→M3U Conversion
43+
44+
When converting from **PyRadio** CSV to M3U format, certain characters are automatically converted for better compatibility and visual presentation:
45+
46+
| Original Character | Converted To | Purpose |
47+
|-------------------|--------------|---------|
48+
| Comma (`,`) | Middle dot (`·`) with spaces | Prevents parsing issues in M3U files |
49+
| Hyphen (`-`) | En dash (``) with spaces | Improved visual appearance |
50+
| Straight quote (`"`) | Right double quotation mark (``) | Better typography |
51+
52+
These conversions help maintain data integrity while ensuring the M3U files work reliably across different media players.
53+
54+
## Field Correspondence
55+
56+
Here's how PyRadio's internal fields map to M3U attributes:
57+
58+
| PyRadio Field | M3U Equivalent | Description |
59+
|---------------|----------------|-------------|
60+
| `Station.name` | `#EXTINF` title | The display name of the radio station |
61+
| `Station.url` | Direct URL line | The stream URL (after `#EXTINF` line) |
62+
| `Station.encoding` | `#PYRADIO-ENCODING` | Character encoding specification |
63+
| `Station.icon` | `tvg-logo` attribute or `#EXTIMG` | Station icon image URL |
64+
| `Station.profile` | `#PYRADIO-PROFILE` | Audio profile setting |
65+
| `Station.buffering` | `network-caching` + `#PYRADIO-BITRATE` | Buffer and bitrate settings |
66+
| `Station.http` | `#PYRADIO-HTTP` | Force HTTP option (special PyRadio feature) |
67+
| `Station.volume` | `#PYRADIO-VOLUME` | Volume preset for the station |
68+
| `Station.referer` | `http-referrer` option | HTTP referer header for the stream |
69+
| `Station.player` | `#PYRADIO-PLAYER` | Preferred media player for this station |
70+
71+
Group information is preserved through M3U's `group-title` attribute and/or `#EXTGRP` directives.
72+
73+
All **#PYRADIO-*** fields inserted in created M3Us are there to preserve **PyRadio** specific data; they will be ignored by all existing players but will be used if the M3U is converted back to a CSV.
74+
75+
## Command Line Usage
76+
77+
The conversion functionality is accessible through PyRadio's command line interface:
78+
79+
```bash
80+
# Convert M3U to CSV
81+
pyradio --convert playlist.m3u
82+
83+
# Convert online M3U to CSV
84+
pyradio --convert https://radio-site.org/m3u/playlist.m3u
85+
86+
# Convert CSV to M3U
87+
pyradio --convert radio.csv
88+
89+
# Specify output file for conversion
90+
pyradio --convert radio -o my_playlist.m3u
91+
92+
# Force overwrite without confirmation
93+
pyradio --convert radio -o my_playlist -y
94+
95+
# Limit the number of processed stations
96+
# This one actually extends it from 10,000 to 50,000
97+
pyradio --convert large_playlist.m3u -lm 50000
98+
99+
```

pyradio/main.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -404,20 +404,23 @@ def shell():
404404

405405
gr_m3u = parser.add_argument_group('• m3u playlist handling')
406406
gr_m3u.add_argument('-cvt', '--convert', default='',
407-
help='Convert CSV (PyRadio playlist) to m3u and vise-versa, based on the file extension of CONVERT. '
407+
help='Convert CSV (PyRadio playlist) to M3U and vise-versa, based on the file extension of CONVERT. '
408408
'If there\'s no file extension, .csv is assumed. '
409-
'Accepts -y, -o (general options). With -o: provides '
410-
'the output file for the CSV to m3u conversion. '
409+
'Accepts -y, -o, -lm (general options). With -o: provides '
410+
'the output file for the CSV to M3U conversion. '
411411
'If not specified, the same path (including the name) '
412412
'as the CONVERT parameter is used, replacing .csv with .m3u. '
413-
'The file extension .m3u will be automatically added if not specified.'
413+
'The file extension .m3u will be automatically added if not specified. '
414+
'With -lm: specify maximum number of stations in an M3U file (default is 10,000).'
414415
)
415416

416417
gr_general = parser.add_argument_group('• options')
417418
gr_general.add_argument('-o', '--output', default='',
418-
help='Output file path (see specific commands for default behavior)')
419+
help='Output file path (see specific commands for default behavior).')
419420
gr_general.add_argument('-y', '--yes', '--force', action='store_true',
420-
help='Assume yes to all prompts (dangerous: overwrites files without confirmation, etc.)')
421+
help='Assume yes to all prompts (dangerous: overwrites files without confirmation, etc.).')
422+
gr_general.add_argument('-lm', '--limit', default='',
423+
help='Use LIMIT as a maximim value of accected items.')
421424

422425
args = parser.parse_args()
423426
sys.stdout.flush()
@@ -505,7 +508,12 @@ def shell():
505508

506509
print(f'[green]Success:[/green] Created M3U file: "{out_file}"')
507510
else:
508-
stations, error = parse_m3u(in_file)
511+
try:
512+
max_entries = int(args.limit) if args.limit else 10_000
513+
except (ValueError, TypeError):
514+
print(f'[red]Error:[/red] Invalid max entries value "{args.limit}"')
515+
sys.exit(1)
516+
stations, error = parse_m3u(in_file, max_entries=max_entries)
509517
if error:
510518
print(f'[red]Error:[/red] {error}')
511519
sys.exit(1)

0 commit comments

Comments
 (0)