Portál AbcLinuxu, 30. dubna 2025 11:25
Nedávno se tu objevil dotaz na hromadné stahování audia z webu mujrozhlas.cz. Já také občas poslouchám jejich pořady a měl jsem v plánu si takový skript někdy napsat. Níže je skript, který jsem vytvořil. Pod ním najdete více okomentovaný kód, s komentáři česky – ne každý umí Perl.
#! /usr/bin/env perl # mujrozhlas.pl – stahuje pořady z webu mujrozhlas.cz # Copyright (C) 2021 jiwopene (https://www.abclinuxu.cz/lide/jiwopene) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. use strict; use v5.13; use utf8; use Cwd; use File::Copy qw(move); use File::Fetch; use Getopt::Long; use HTML::TagParser; use HTTP::Tiny; use JSON::PP; my $download_unknown_urls = 0; my $overwrite_files = 0; my $skip_audio_download = 0; my $write_yaml = 0; my $make_subdirs = 0; my $output_root_dir = getcwd; # Děkuji za připomenutí tohoto řádku: # see https://www.abclinuxu.cz/poradna/linux/show/468500#26 binmode STDOUT, ':utf8'; binmode STDERR, ':utf8'; # Download something using HTTP(S) and return its response text. # # my $response_text = download($url); sub download { my ($url) = @_; my $resp = HTTP::Tiny->new->get($url); die "Nemůžu stáhnout $url: $resp->{status} $resp->{reason}\n" unless $resp->{success}; return $resp->{content}; } # Get episode metadata as hashmap. # # my %meta = get_episode_meta($episode_uuid); sub get_episode_meta { my ($episode_uuid) = @_; return decode_json download "https://api.mujrozhlas.cz/episodes/$episode_uuid"; } # Download episode to file with given name base. # # download_episode $name_base, $episode_meta; sub download_episode { my ($name_base, $episode_meta) = @_; my $audio_links = $episode_meta->{data}->{attributes}->{audioLinks}; for my $audio_link ( @$audio_links ) { my $audio_file_name = "$name_base.$audio_link->{bitrate}.$audio_link->{variant}"; $audio_file_name =~ tr@/@-@; unless ($skip_audio_download) { if (-e $audio_file_name) { if ($overwrite_files) { unlink $audio_file_name; } else { say "$audio_file_name již existuje, přeskakuji."; next; } } say "Stahuji $audio_file_name"; my $fetch = File::Fetch->new( uri => $audio_link->{url} ); unless ($fetch) { say "Přeskakuji $audio_link->{url}"; next; } my $where = $fetch->fetch(to => '/tmp'); unless ($where) { say "Nemůžu stáhnout $fetch->uri"; next; } move $where, "./$audio_file_name"; } else { say 'Stahování audia vypnuto, přeskakuji.'; } } } sub episode_info_from_elem { my ($episode_elem) = @_; my $episode_info_json = $episode_elem->getAttribute('data-entry'); my $episode_info = decode_json $episode_info_json; } # Download series with some url. # # URL example: # https://www.mujrozhlas.cz/stripky-z-archivu/stare-povesti-ceske # # download_series_by_url $page_url; sub download_series_by_url { my ($page_url) = @_; if (not $download_unknown_urls and $page_url !~ m@^https?://(www.)?mujrozhlas.cz/@) { say "URL stránky $page_url nevypadá platně, přeskakuji."; return 0; } my $page_html = download $page_url; my $page = HTML::TagParser->new($page_html); my @episode_elems; # Create and go into subdir if asked to do so. if ($make_subdirs) { my $detail_title_elem = $page->getElementById('detail-title'); my $episode_dir = $detail_title_elem->innerText; $episode_dir =~ tr@/@-@; mkdir $episode_dir unless -e $episode_dir; chdir $episode_dir; } # Get series ID. my $more_link_elem = $page->getElementsByClassName('more-link__link ajax'); if ($more_link_elem) { my $more_episodes_url = $more_link_elem->getAttribute('href'); # in format /ajax/ajax_list/FOO?page=1&size=9&id=FOO-1234&rid=1234 $more_episodes_url =~ m@ajax_list/(\w+)@; my $show_class = $1; $more_episodes_url =~ /[&?]id=(.*?)([&?]|$)/; my $show_id = $1; $more_episodes_url =~ /[&?]rid=(.*?)([&?]|$)/; my $show_rid = $1; my $episode_links_response = decode_json download "https://www.mujrozhlas.cz/ajax/ajax_list/$show_class?id=$show_id&rid=$show_rid&size=1000000"; # unset size= gives 20 entries my $episode_links_html = $episode_links_response->{snippets}->{$show_id}->{content}; my $episodes_page = HTML::TagParser->new($episode_links_html); @episode_elems = $episodes_page->getElementsByClassName("b-episode"); } else { # More link not available, fallback to direct HTML analysis. say 'Nemohu najít odkaz na více episod. Stahuji to co mám.'; @episode_elems = $page->getElementsByClassName("b-episode"); } for my $episode_elem ( @episode_elems ) { my $episode_info = episode_info_from_elem $episode_elem; say ''; say "#$episode_info->{id}: $episode_info->{title}"; my $episode_meta = get_episode_meta $episode_info->{uuid}; my $name_base = "$episode_info->{id}_$episode_info->{title}"; $name_base =~ tr@/@-@; if ($write_yaml) { YAML::PP->new->dump_file("$name_base.yaml", $episode_meta); } else { open my $meta_file, ">", "$name_base.json"; print $meta_file encode_json $episode_meta; close $meta_file; } download_episode $name_base, $episode_meta; } # Go back. chdir $output_root_dir; return 1; } sub print_help { $_ = "$0 -- strahování pořadů z mujrozhlas.cz Použití: perl $0 https://mujrozhlas.cz/lorem/ipsum Volby: --help Zobrazí tuto nápovědu. --unknown-urls Pokusí se stáhovat i z adres, které nevypadají platně. --overwrite Přepisuje soubory místo přeskočení existujících. --no-audio Bude stahovat jen metadata. --write-yaml Ukládá metadata jako YAML, ne JSON. --subdirs Vytvoří podadresář pro každý pořad. "; chomp; say; } my $show_help = 0; GetOptions( 'help' => \$show_help, 'unknown-urls' => \$download_unknown_urls, 'overwrite' => \$overwrite_files, 'no-audio' => \$skip_audio_download, 'write-yaml' => \$write_yaml, 'subdirs' => \$make_subdirs, ) or die 'Neplatné volby. Viz $0 --help.'; require YAML::PP if $write_yaml; if ($show_help) { print_help; exit; } say "mujrozhlas.pl, verze 1.1"; for my $url (@ARGV) { download_series_by_url $url; }
Použití je jednoduché: skriptu předáme jako argumenty jednu nebo více URL pořadů na webu
https://www.mujrozhlas.cz/
. Například takto:
perl mujrozhlas.pl https://www.mujrozhlas.cz/stripky-z-archivu/stare-povesti-ceske https://www.mujrozhlas.cz/bijacek
Skript používá také několik dlouhých voleb. --help
zobrazí nápovědu (vizte konec kódu, nechcete-li ho
spouštět). Ve výchozím nastavení ukládá metadata (JSON, případně YAML) do souboru s názvem ve tvaru
1234_Název episody.json
(resp. 1234_Název episody.yaml
) a zvuk do souboru s názvem ve tvaru
1234_Název episody.bitrate.typ
, kde 1234 je číselný identifikátor episody. Stahování audia vypneme volbou
--no-audio
. Volba --subdirs
vytváří pro pořady podadresáře.
Audio je stahované do dočasných souborů a poté překopírované do cílového adresáře. Pokud již soubor s audiem existuje,
přeskakuje se, ale soubor s metadaty se přepíše. Volba --overwrite
smaže případný soubor s audiem před
stažením.
Skript provádí základní ověření tvaru URL. Pokdu ho chcete obejít, zapněte --unknown-urls
, což toto ověření
vypne. To můžete použít například při stahování audia, ke kterému máte soubor HTML na nějakém zrcadle a nemáte původní
URL.
Skript je psaný v jazyce Perl. Ten je obzvlášť vhodný pro zpracovávání textů.¹ Obsahuje například podporu pro regulární výrazy včetně operátorů pro práci s nimi, atd. Vzhledem k tomu, že tazatel v diskusi napsal, že Perlu moc nerozumí, tak základy vysvětlím. Předpokládám alespoň základní znalosti programování.
Komentáře jsou od #
po konec řádku. Stringy píšeme do uvozovek (s expanzí proměnných), mezi apostrofy (bez
ní) nebo jiným způsobem (viz níže). Je dynamicky typovaný. Boolean neexistuje – prázdný string nebo 0 je false, jiné
hodnoty (s pár vyjímkami) jsou true.
Podmínky píšeme do if (podmínka) { tělo; }
(jako v C). Když napíšeme místo if
unless
, vyhodnocujue se negace podmínky (prohodí se větve if a else). Podmínku můžeme psát i za
statement, a to takto: télo if podmínka;
(případně s unless). Cyklus for-each se zapisuje takto: for
proměnná (pole) { tělo; }
.
Řádky s use
načítají balíčky (knihovny), případně určují verzi interpretru nebo jiná nastavení.
Názvy proměnných začínají sigilem: $
pro skaláry, @
pro pole a %
pro hashmapy.
Pokud čteme skalární prvek pole/hashmapy, píšeme $
. K metodám přistupujeme přes ->
,
k hodnotě z hashmapy pomocí ->{klíč}
(klíč je buď napsaný přímo, nebo jako string). K věcem v balíčku se
přistupuje pomocí ::
.
Lokální proměnné deklarujeme pomocí klíčového slova my
Subrutiny (funkce) vytváříme pomocí sub název { tělo }
. Argumenty dostane předané v poli @_
.
Regulární výrazy píšeme do /lomítek/, nahrazení s prefixem s
: s/co/čím/volby
. Jakmile píšeme
regulárný výrazy nebo stringy s tím prefixem (s, m, q, qw, qq, tr, …), můžeme zvolit i jiný znak než lomítka (závorky,
zavináče, …), takže nemusíme escapovat lomítka. Prefix s
uvozuje nahrazení, q
a qq
string (jako by byl s jednoduchými nebo dvojitými uvozovkami – dle počtu Qček), tr
nahrazení znaků (jako shellový příkaz tr(1)
), qw
pole stringů (slov), m
regulární
výraz.
Komentáře přidané do předchozího kódu jsou značené #-
.
#! /usr/bin/env perl # mujrozhlas.pl – stahuje pořady z webu mujrozhlas.cz # Copyright (C) 2021 jiwopene (https://www.abclinuxu.cz/lide/jiwopene) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. use strict; #- Více chyb. use v5.13; #- Verze Perlu. use utf8; #- Kód obsahue UTF-8. #- Knihovny. use Cwd; use File::Copy qw(move); use File::Fetch; use Getopt::Long; use HTML::TagParser; use HTTP::Tiny; use JSON::PP; #- Sem ukládáme volby z příkazové řádky. my $download_unknown_urls = 0; my $overwrite_files = 0; my $skip_audio_download = 0; my $write_yaml = 0; my $make_subdirs = 0; #- Uložíme si adresář při spuštění, abychom se sem mohli vrátit při stahování do podadresáře. my $output_root_dir = getcwd; #- stdout a stderr jsou v UTF-8. # Děkuji za připomenutí tohoto řádku: # see https://www.abclinuxu.cz/poradna/linux/show/468500#26 binmode STDOUT, ':utf8'; binmode STDERR, ':utf8'; # Download something using HTTP(S) and return its response text. # # my $response_text = download($url); sub download { #- Rozbijeme argumenty do proměnných. my ($url) = @_; my $resp = HTTP::Tiny->new->get($url); die "Nemůžu stáhnout $url: $resp->{status} $resp->{reason}\n" unless $resp->{success}; return $resp->{content}; } # Get episode metadata as hashmap. # # my %meta = get_episode_meta($episode_uuid); sub get_episode_meta { #- Rozbijeme argumenty do proměnných. my ($episode_uuid) = @_; return decode_json download "https://api.mujrozhlas.cz/episodes/$episode_uuid"; } # Download episode to file with given name base. # # download_episode $name_base, $episode_meta; sub download_episode { #- Rozbijeme argumenty do proměnných. my ($name_base, $episode_meta) = @_; my $audio_links = $episode_meta->{data}->{attributes}->{audioLinks}; for my $audio_link ( @$audio_links ) { #- Pro každou položku audioLink z metadat, #- Vygeneruj název souboru. my $audio_file_name = "$name_base.$audio_link->{bitrate}.$audio_link->{variant}"; #- Nahraď lomítka ASCII mínusy. $audio_file_name =~ tr@/@-@; #- Pokud nepřeskakujeme stahování audia, unless ($skip_audio_download) { # tak, pokud exisuje soubor s audieam if (-e $audio_file_name) { if ($overwrite_files) { #- a přepisujeme soubory unlink $audio_file_name; #- ho smažeme. } else { #- Jinak si postěžujeme a ... say "$audio_file_name již existuje, přeskakuji."; #- jdeme na další audioLink. next; } } say "Stahuji $audio_file_name"; #- Stáhneme URL do dočasného souboru $where (viz níže). my $fetch = File::Fetch->new( uri => $audio_link->{url} ); unless ($fetch) { #- Při chybě. say "Přeskakuji $audio_link->{url}"; next; } my $where = $fetch->fetch(to => '/tmp'); unless ($where) { #- Při chybě. say "Nemůžu stáhnout $fetch->uri"; next; } #- Přesuneme soubot na příslušné místo v pracovním adresáři. move $where, "./$audio_file_name"; } else { say 'Stahování audia vypnuto, přeskakuji.'; } } } sub episode_info_from_elem { my ($episode_elem) = @_; #- Vyčteme JSON se základními metadaty z HTML elementu. my $episode_info_json = $episode_elem->getAttribute('data-entry'); my $episode_info = decode_json $episode_info_json; } # Download series with some url. # # URL example: # https://www.mujrozhlas.cz/stripky-z-archivu/stare-povesti-ceske # # download_series_by_url $page_url; sub download_series_by_url { #- Rozbijeme argumenty do proměnných. my ($page_url) = @_; #- Ověření platnosti URL. if (not $download_unknown_urls and $page_url !~ m@^https?://(www.)?mujrozhlas.cz/@) { say "URL stránky $page_url nevypadá platně, přeskakuji."; return 0; } #- Stáhnu a naparsuji HTML stránky. my $page_html = download $page_url; my $page = HTML::TagParser->new($page_html); my @episode_elems; #- Sem přijdou elementy s metadaty. # Create and go into subdir if asked to do so. if ($make_subdirs) { #- Vyčteme titulek a nahradíme lomítka ASCII mínusy. my $detail_title_elem = $page->getElementById('detail-title'); my $episode_dir = $detail_title_elem->innerText; $episode_dir =~ tr@/@-@; #- Vytvoříme adresář pokud neexistuje (not -e). mkdir $episode_dir unless -e $episode_dir; #- Vstoupíme do něj. chdir $episode_dir; } # Get series ID. my $more_link_elem = $page->getElementsByClassName('more-link__link ajax'); if ($more_link_elem) { #- Pokud je na stránce odkaz na více episod, vezmeme jeho adresu, upravíme ji pro získání #- max. 1000000 záznamů a stáhneme HTML z ní (to je zabalené v JSONu), jinak ho bereme přímo #- ze stažené stránky. my $more_episodes_url = $more_link_elem->getAttribute('href'); # in format /ajax/ajax_list/FOO?page=1&size=9&id=FOO-1234&rid=1234 $more_episodes_url =~ m@ajax_list/(\w+)@; my $show_class = $1; #- $1 je první skupina z regexu. $more_episodes_url =~ /[&?]id=(.*?)([&?]|$)/; my $show_id = $1; #- $1 je první skupina z regexu. $more_episodes_url =~ /[&?]rid=(.*?)([&?]|$)/; my $show_rid = $1; #- $1 je první skupina z regexu. #- Vytvoříme si nové URL a stáhneme ho. my $episode_links_response = decode_json download "https://www.mujrozhlas.cz/ajax/ajax_list/$show_class?id=$show_id&rid=$show_rid&size=1000000"; # unset size= gives 20 entries #- Vyndáme z JSONu HTML, my $episode_links_html = $episode_links_response->{snippets}->{$show_id}->{content}; #- naparsujeme ho a ... my $episodes_page = HTML::TagParser->new($episode_links_html); #- vezmeme si z něj elementy se třídou b-episode. @episode_elems = $episodes_page->getElementsByClassName("b-episode"); } else { # More link not available, fallback to direct HTML analysis. say 'Nemohu najít odkaz na více episod. Stahuji to co mám.'; @episode_elems = $page->getElementsByClassName("b-episode"); } #- Pro každý element s odkazem an episodu, for my $episode_elem ( @episode_elems ) { my $episode_info = episode_info_from_elem $episode_elem; say ''; #- Odřádkuji. say "#$episode_info->{id}: $episode_info->{title}"; #- Napíšu ID a název episody. my $episode_meta = get_episode_meta $episode_info->{uuid}; #- Získám metadata episody. #- Vydělám si název souboru a ... my $name_base = "$episode_info->{id}_$episode_info->{title}"; #- nahradím lomítka ASCII mínusy. $name_base =~ tr@/@-@; #- Uložím metadata. if ($write_yaml) { YAML::PP->new->dump_file("$name_base.yaml", $episode_meta); } else { open my $meta_file, ">", "$name_base.json"; print $meta_file encode_json $episode_meta; close $meta_file; } #- Stáhnu episodu. download_episode $name_base, $episode_meta; } # Go back. chdir $output_root_dir; return 1; #- 1 je úspěch. } sub print_help { $_ = "$0 -- strahování pořadů z mujrozhlas.cz Použití: perl $0 https://mujrozhlas.cz/lorem/ipsum Volby: --help Zobrazí tuto nápovědu. --unknown-urls Pokusí se stáhovat i z adres, které nevypadají platně. --overwrite Přepisuje soubory místo přeskočení existujících. --no-audio Bude stahovat jen metadata. --write-yaml Ukládá metadata jako YAML, ne JSON. --subdirs Vytvoří podadresář pro každý pořad. "; chomp; #- Odstaním newline z konce. say; #- Vypíšu. } my $show_help = 0; GetOptions( 'help' => \$show_help, 'unknown-urls' => \$download_unknown_urls, 'overwrite' => \$overwrite_files, 'no-audio' => \$skip_audio_download, 'write-yaml' => \$write_yaml, 'subdirs' => \$make_subdirs, ) or die 'Neplatné volby. Viz $0 --help.'; require YAML::PP if $write_yaml; #- require jde dát do podmínky, use ne. if ($show_help) { print_help; exit; } say "mujrozhlas.pl, verze 1.1"; for my $url (@ARGV) { download_series_by_url $url; }
¹ To je můj názor. Zcela jistě si někteří myslí něco jiného.
Tiskni
Sdílej:
supr skriptík užitečnej dík :D ;D
Ten je obzvlášť vhodný pro zpracovávání textů
na dělání regexpů je prej vo moc víc rychlejší než python třeba :O :O maďarskej krejčí to jakoby měřil hele :O ;D
810705_Osudy dobrého vojáka Švejka (11-13).128.mp3 810708_Osudy dobrého vojáka Švejka (4-13).256.mp3 810714_Osudy dobrého vojáka Švejka (3-13).256.mp3 810717_Osudy dobrého vojáka Švejka (1-13).128.mp3 810720_Osudy dobrého vojáka Švejka (10-13).128.mp3 810723_Osudy dobrého vojáka Švejka (6-13).128.mp3 810729_Osudy dobrého vojáka Švejka (5-13).256.mp3 810732_Osudy dobrého vojáka Švejka (9-13).128.mp3 810735_Osudy dobrého vojáka Švejka (8-13).128.mp3 810738_Osudy dobrého vojáka Švejka (2-13).128.mp3 810741_Osudy dobrého vojáka Švejka (13-13).128.mp3 810744_Osudy dobrého vojáka Švejka (7-13).128.mp3 810747_Osudy dobrého vojáka Švejka (12-13).128.mp3Logictejsi by bylo neco jako:
Osudy dobrého vojáka Švejka (01/13)[810717].mp3 Osudy dobrého vojáka Švejka (02/13)[810738].mp3Ale jinak diky moc za skriptik!
když sem to jako maďaroj dycky nazačátku zkompilovala hele vtom pythonu skriptík už nebyl pomalej 20x ale asi jakoby jenom 4-5x takže ztoho ten perl asi furt de jako víc lepšejší děladlo s textama :D ;D
supr ;D
ffmpeg -i "https://croaod.cz/stream/sem-dej-uuid.m4a/manifest.mpd" -map 0:0 -codec copy jmeno-souboru.m4a
Nemůžu stáhnout https://www.mujrozhlas.cz/radiokniha/marketa-hejkalova-dum-pod-namestim-vypraveni-jednoho-domu: 599 Internal ExceptionTrochu jsem googlil, našel jsem něco o firewallu na druhé straně. Kdyby někdo věděl, co s tím, budu rád. Jirka
mujrozhlas.pl, verze 1.1
URL stránky https://vltava.rozhlas.cz/joseph-heller-hlava-xxii-vyznamny-roman-kritizujici-nesmyslnost-valky-cernym-8976495 nevypadá platně, přeskakuji.
Poradíte?
TP
https:\/\/croaod.cz\/stream\/e619958d-d4db-4327-8b44-0ccf89cd762c.m4a\/manifest.mpd
wget -O - $URL | grep -o 'https:\\/\\/croaod.cz\\/stream\\/[^"]*\.mpd' rozhlas.html | sed 's/\\/\//g'| sed 's/\/\//\//g'
https://vltava.rozhlas.cz/joseph-heller-hlava-xxii-vyznamny-roman-kritizujici-nesmyslnost-valky-cernym-8976495
https://croaod.cz/stream/f35b9e13-faf6-46f4-8863-ac8a9ac09c3e.m4a/manifest.mpd
https://croaod.cz/stream/e619958d-d4db-4327-8b44-0ccf89cd762c.m4a/manifest.mpd
https://croaod.cz/stream/dab2edf1-d6f5-498e-9629-1cdeebe45144.m4a/manifest.mpd
https://croaod.cz/stream/045d955e-02be-4ab8-99ce-924b80801294.m4a/manifest.mpd
https://croaod.cz/stream/24b3866a-11f3-4d5c-b052-480c7ac04ded.m4a/manifest.mpd
https://croaod.cz/stream/2c2e40c7-89d4-4cc7-9d4b-c3929be8f7ea.m4a/manifest.mpd
https://croaod.cz/stream/94275875-6739-41eb-97d3-fcc3f231e349.m4a/manifest.mpd
https://croaod.cz/stream/b57ee671-42d8-4aa6-b6bb-3058f493a022.m4a/manifest.mpd
https://croaod.cz/stream/adec9233-4d4e-4a37-bce5-fb83b9309edc.m4a/manifest.mpd
https://croaod.cz/stream/61d9f93e-4b09-4537-91e1-c55c4c898c60.m4a/manifest.mpd
https://croaod.cz/stream/88b32d69-b480-46cc-888d-b918fe5455fe.m4a/manifest.mpd
https://croaod.cz/stream/0a3fb2cf-7316-4f7d-99ee-deccaa606d94.m4a/manifest.mpd
https://croaod.cz/stream/af73186e-a29c-4d23-a230-a291214f1230.m4a/manifest.mpd
https://croaod.cz/stream/ba975d10-6675-47ea-a8db-709bcd1ae5ce.m4a/manifest.mpd
https://croaod.cz/stream/ae8e728d-9b64-4282-9876-a69eb696149c.m4a/manifest.mpd
https://croaod.cz/stream/4ca55324-a703-42a1-bbcf-7d6882266c5e.m4a/manifest.mpd
https://croaod.cz/stream/ea756c97-d2c0-46c3-9e06-2e09607eca18.m4a/manifest.mpd
https://croaod.cz/stream/b80dc85e-02f9-4610-bf3e-107d6b1ea433.m4a/manifest.mpd
https://croaod.cz/stream/00bdcd56-7060-48f2-a908-8d882d758f67.m4a/manifest.mpd
https://croaod.cz/stream/3cf01aa1-9bc0-4522-a88b-7f9d9255c45f.m4a/manifest.mpd
https://croaod.cz/stream/21c7642d-a8fc-4741-9b84-bac4969fda05.m4a/manifest.mpd
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.