Overview
Submit package home:develop7:branches:Extra / get_iplayer to package Extra / get_iplayer
get_iplayer.changes
Changed
x
1
2
-------------------------------------------------------------------
3
+Tue Feb 8 10:40:09 UTC 2022 - Andrei Dziahel <develop7@develop7.info>
4
+
5
+- get_iplayer 3.29
6
+
7
+ See https://github.com/get-iplayer/get_iplayer/wiki/release320to329#release329
8
+ for upstream release notes
9
+
10
+-------------------------------------------------------------------
11
Mon Feb 7 18:56:44 UTC 2022 - Andrei Dziahel <develop7@develop7.info>
12
13
- get_iplayer v3.28
14
get_iplayer.spec
Changed
10
1
2
#
3
4
Name: get_iplayer
5
-Version: 3.28
6
+Version: 3.29
7
Release: 0
8
Summary: Downloads H.264 BBC IPlayer TV, Radio, and Podcast Programs
9
License: GPL-3.0-only
10
get_iplayer-3.28.tar.gz/.github/ISSUE_TEMPLATE/bug_report.md
Deleted
37
1
2
----
3
-name: Bug report
4
-about: Create a bug report
5
-title: ''
6
-labels: ''
7
-assignees: ''
8
-
9
----
10
-### *Remove this line and all the text below before submitting your bug report*
11
-
12
-#### Read first
13
-
14
-- Do not request help with using get_iplayer. No user support will be provided.
15
-- Do not request new features. Feature requests will not be accepted.
16
-- All bug reports will automatically be closed and locked upon receipt.
17
-- If your report identifies a reproducible bug in get_iplayer, it will be re-opened until a fix is released.
18
-- You will receive no communication from the developers, so provide all the information required.
19
-
20
-#### What you need to provide
21
-
22
-- A clear and concise description of the bug.
23
-- The **complete get_iplayer command line** used.
24
-- The PID or URL of the programme you attempted to download, if applicable. **Only provide one programme**.
25
-- A complete verbose log. Add logs as attachments. Do not paste logs into your bug report. Create a verbose log [e.g., log.txt] with:
26
-
27
- get_iplayer [your options here] --verbose > log.txt 2>&1
28
-
29
-- Screenshots, if the bug appears to be in the Web PVR Manager user interface.
30
-- OS and version [e.g. Windows 10 2004, macOS 10.15.5, Ubuntu 20.04.1]
31
-- Browser and version [e.g. Chrome 83.0.4103, Edge 83.0.478.56, Firefox 78.0.1, Safari 13.1.1]
32
-- get_iplayer version [e.g. 3.26, 3.26.0-MSWin32, 3.26.1-darwin] - use `get_iplayer -V`
33
-
34
-#### Documentation changes
35
-
36
-If you would like to contribute documentation changes, submit a bug report with your suggested changes and provide the full URL for the wiki page you wish to change. Direct editing of the wiki has been restricted to project developers due to attempted link hijacking by spambots.
37
get_iplayer-3.29.tar.gz/.github/ISSUE_TEMPLATE/bug_report.yaml
Added
88
1
2
+name: Bug Report
3
+description: File a bug report
4
+body:
5
+- type: markdown
6
+ attributes:
7
+ value: |
8
+ ## Read First
9
+ * Do not request help with using get_iplayer. User support will not be provided.
10
+ * Do not request new features for get_iplayer. Feature requests will not be accepted.
11
+ * All bug reports will automatically be closed and locked upon receipt.
12
+ * If your report identifies a reproducible bug in get_iplayer, it will be re-opened until a fix is released.
13
+ * You will receive no communication from the developers, so provide all the information required.
14
+- type: checkboxes
15
+ attributes:
16
+ label: Do not file a bug report if you are using get_iplayer outside the UK. If you do, your report will be ignored.
17
+ options:
18
+ - label: I am not using get_iplayer outside the UK
19
+ required: true
20
+- type: checkboxes
21
+ attributes:
22
+ label: Do not file a bug report if you are using get_iplayer with a VPN or proxy from any location, including the UK. If you do, your report will be ignored.
23
+ options:
24
+ - label: I am not using get_iplayer with a VPN or proxy from any location, including the UK
25
+ required: true
26
+- type: checkboxes
27
+ attributes:
28
+ label: Search the repository (search field at top left) to see if a report already exists for the bug in the issue tracker. Do not create a duplicate report. Duplicate reports will be ignored.
29
+ description: See - **[Issue Tracker](https://github.com/get-iplayer/get_iplayer/issues)**
30
+ options:
31
+ - label: I have searched the repository and found no existing reports of the bug in the issue tracker
32
+ required: true
33
+- type: checkboxes
34
+ attributes:
35
+ label: Review recent open and closed entries in the issue tracker to see if a report already exists for the bug. Do not create a duplicate report. Duplicate reports will be ignored.
36
+ description: See - [Open Issues](https://github.com/get-iplayer/get_iplayer/issues?q=is%3Aopen+is%3Aissue), [Closed Issues](https://github.com/get-iplayer/get_iplayer/issues?q=is%3Aclosed+is%3Aissue)
37
+ options:
38
+ - label: I have reviewed recent open and closed entries in the issue tracker and found no existing reports of the bug
39
+ required: true
40
+- type: checkboxes
41
+ attributes:
42
+ label: Ensure that you are using get_iplayer 3.29 or higher. If not, your report will be ignored.
43
+ description: Check version with **`get_iplayer -V` or see the bottom of the Web PVR search page**
44
+ options:
45
+ - label: I am using get_iplayer 3.29 or higher
46
+ required: true
47
+- type: input
48
+ attributes:
49
+ label: Identify the operating system and version where get_iplayer demonstrates the bug
50
+ description: Examples - Ubuntu 20.04, Fedora 34, Arch, OpenBSD 6.8, macOS 10.14, macOS Monterey (M1), Windows 10
51
+ validations:
52
+ required: true
53
+- type: textarea
54
+ attributes:
55
+ label: Provide a clear and concise description of the bug. Do not paste get_iplayer output or screenshots into the field below. If you do, your report will be ignored.
56
+ validations:
57
+ required: true
58
+- type: input
59
+ attributes:
60
+ label: Provide the PID or URL of the programme you are attempting to download, if applicable. Provide the PID or URL for only one programme.
61
+ validations:
62
+ required: false
63
+- type: textarea
64
+ attributes:
65
+ label: Provide the complete get_iplayer command line that demonstrates the bug. Do not truncate or excerpt the command. If you do, your report will be ignored. If you are using the Web PVR, list the steps necessary to reproduce the bug, in as much detail as possible.
66
+ validations:
67
+ required: true
68
+- type: markdown
69
+ attributes:
70
+ value: |
71
+ ## Create a verbose log file for the bug, e.g., "log.txt" using the `--verbose` option:
72
+ ```bash
73
+ get_iplayer [your options here] --verbose > log.txt 2>&1
74
+ ```
75
+ ### Do not truncate or excerpt the log file. If you do, your report will be ignored. If you are using the Web PVR, copy output from both your web browser and the Web PVR console window and paste it into a plain text file. Do not omit the console window output showing the get_iplayer command(s) invoked by the Web PVR. If you do, your report will be ignored. To minimise the amount of output in the console window, quit and restart the Web PVR, then immediately execute the command that demonstrates the bug.
76
+ ### Do not submit screenshots instead of a log file. If you do, your report will be ignored.
77
+- type: textarea
78
+ attributes:
79
+ label: Drag the log file into the field below to create an attachment. The log file must be added as an attachment. Do not paste its contents into the field below. If you do, your report will be ignored. You may enter a URL linking to your log file on a pastebin site. If the bug prevents get_iplayer from running, enter "N/A" in the field below. If you enter any other text in the field below, your report will be ignored.
80
+ validations:
81
+ required: true
82
+- type: input
83
+ attributes:
84
+ label: If you are using the Web PVR, provide your web browser name and version. This information typically can be found from the application menu via Chrome/Firefox/Safari->About... (macOS) or Help->About... (Linux/Windows)
85
+ description: Examples - Chrome 83.0.4103, Edge 83.0.478.56, Firefox 78.0.1, Safari 13.1.1
86
+ validations:
87
+ required: false
88
get_iplayer-3.29.tar.gz/.github/PULL_REQUEST_TEMPLATE.md
Added
13
1
2
+### Read first
3
+
4
+- Fixes for reproducible bugs are welcome.
5
+- Do not submit new features. New features will not be merged.
6
+- Do not submit enhancements. Enhancements will not be merged.
7
+- Do not submit changes to existing behaviour. Changes to existing behaviour will not be merged.
8
+- All pull requests will automatically be closed and locked upon receipt.
9
+- If your pull request fixes a reproducible bug, it will be re-opened until it is merged.
10
+- You will receive no communication from the developers, so ensure your pull request is complete.
11
+
12
+#### Delete this line and all text above before submitting your pull request.
13
get_iplayer-3.29.tar.gz/.github/workflows/auto-close-lock-pr-action.yml
Added
31
1
2
+on:
3
+ pull_request:
4
+ types:
5
+ - opened
6
+jobs:
7
+ auto-close-lock-pr-action_job:
8
+ runs-on: ubuntu-latest
9
+ name: auto-close-lock-pr-action_job
10
+ steps:
11
+ - name: auto-close-lock-pr-action_close-step
12
+ id: auto-close-lock-pr-action_close-step
13
+ uses: maxkomarychev/octions/octions/issues/update@master
14
+ with:
15
+ token: ${{ secrets.GITHUB_TOKEN }}
16
+ issue_number: ${{ github.event.number }}
17
+ state: closed
18
+ - name: auto-close-lock-pr-action_lock-step
19
+ id: auto-close-lock-pr-action_lock-step
20
+ uses: maxkomarychev/octions/octions/issues/lock@master
21
+ with:
22
+ token: ${{ secrets.GITHUB_TOKEN }}
23
+ issue_number: ${{ github.event.number }}
24
+ - name: auto-close-lock-pr-action_label-step
25
+ id: auto-close-lock-pr-action_label-step
26
+ uses: maxkomarychev/octions/octions/issues/add-labels@master
27
+ with:
28
+ token: ${{ secrets.GITHUB_TOKEN }}
29
+ issue_number: ${{ github.event.number }}
30
+ labels: invalid
31
get_iplayer-3.28.tar.gz/Makefile -> get_iplayer-3.29.tar.gz/Makefile
Changed
18
1
2
@git checkout master
3
@sed -i.bak -e 's/^\(my $$version = \).*/\1$(VERSION);/' get_iplayer
4
@sed -i.bak -e 's/^\(my $$VERSION = \).*/\1$(VERSION);/' get_iplayer.cgi
5
- @rm -f get_iplayer.bak get_iplayer.cgi.bak
6
+ @sed -i.bak -e 's/\(get_iplayer \)[0-9.]\{1,\}\( or higher\)/\1$(VERSION)\2/g' .github/ISSUE_TEMPLATE/bug_report.yaml
7
+ @rm -f get_iplayer.bak get_iplayer.cgi.bak .github/ISSUE_TEMPLATE/bug_report.yaml.bak
8
@./get_iplayer --nocopyright --manpage get_iplayer.1
9
@git diff --exit-code get_iplayer.1 > /dev/null || \
10
sed -i.bak -e 's/\(\.TH GET_IPLAYER "1" "\)[^"]*"/\1$(shell date +"%B %Y")\"/' get_iplayer get_iplayer.1
11
@rm -f get_iplayer.bak get_iplayer.1.bak
12
@git log --format='%aN' | sort -u > CONTRIBUTORS; git add CONTRIBUTORS
13
- @git commit -m "Release $(VERSION)" get_iplayer get_iplayer.cgi get_iplayer.1 CONTRIBUTORS
14
+ @git commit -m "Release $(VERSION)" get_iplayer get_iplayer.cgi get_iplayer.1 CONTRIBUTORS .github/ISSUE_TEMPLATE/bug_report.yaml
15
@git tag v$(VERSION)
16
17
tarball:
18
get_iplayer-3.28.tar.gz/README.md -> get_iplayer-3.29.tar.gz/README.md
Changed
59
1
2
3
* Downloads TV and radio programmes from BBC iPlayer/BBC Sounds
4
* Allows multiple programmes to be downloaded using a single command
5
-* Indexing of most available iPlayer/Sounds catch-up programmes from previous 30 days (not BBC Three, Red Button, iPlayer Exclusive, or Podcast-only)
6
+* Indexing of most available iPlayer/Sounds catch-up programmes from previous 30 days (not Red Button, iPlayer Exclusive, or Podcast-only)
7
* Caching of programme index with automatic updating
8
* Regex search on programme name
9
* Regex search on programme description and episode title
10
11
12
*(The `$` regular expression metacharacter matches "Radio 4" only at the end of the channel name, thus avoiding matches against "Radio 4 Extra")*
13
14
-* Record TV programme number 208 (index from search results) in HD, with SD fallback if HD not available:
15
+* Record TV programme number 208 (index from search results) in HD, with fallback to lower quality if not available:
16
17
- `get_iplayer --get 208` [default is to download best available (max 1280x720)]
18
+ `get_iplayer --get 208` [default setting]
19
20
- OR
21
-
22
- `get_iplayer --get 208 --tvmode=best`
23
+ or
24
+
25
+ `get_iplayer --get 208 --tv-quality=hd,sd,web,mobile` [explicit setting]
26
27
-* Record TV programme number 208 with lower resolution (max 704x396):
28
+* Record TV programme number 208 in lower resolution only (704x396@50):
29
30
- `get_iplayer --get 208 --tvmode=good`
31
+ `get_iplayer --get 208 --tv-quality=web`
32
33
* Record TV programme number 208 and download subtitles in SubRip (SRT) format:
34
35
36
* Record a radio programme using its Sounds URL:
37
38
`get_iplayer https://www.bbc.co.uk/sounds/play/b07gcv34`
39
-* Record a radio programme using the PID (b07gcv34) from its Sounds URL in highest quality (320k), with fallback to lower quality if not available:
40
41
- `get_iplayer --pid=b07gcv34` [default is to download best available]
42
+* Record a radio programme using the PID (b07gcv34) from its Sounds URL in high quality (320k), with fallback to lower quality if not available (default setting):
43
+
44
+ `get_iplayer --pid=b07gcv34` [default setting]
45
46
OR
47
48
- `get_iplayer --pid=b07gcv34 --radiomode=best`
49
+ `get_iplayer --pid=b07gcv34 --radio-quality=high,std,med,low` [explicit setting]
50
51
-* Record a radio programme using the PID (b07gcv34) from its Sounds URL with lower bit rate (96k):
52
+* Record a radio programme using the PID (b07gcv34) from its Sounds URL with lower bit rate only (96k):
53
54
- `get_iplayer --pid=b07gcv34 --radiomode=good`
55
+ `get_iplayer --pid=b07gcv34 --radio-quality=med`
56
57
* Record multiple radio programmes (using PIDs from Sounds URLs):
58
59
get_iplayer-3.28.tar.gz/get_iplayer -> get_iplayer-3.29.tar.gz/get_iplayer
Changed
1227
1
2
#
3
#
4
package main;
5
-my $version = 3.28;
6
+my $version = 3.29;
7
my $version_text;
8
$version_text = sprintf("v%.2f", $version) unless $version_text;
9
#
10
11
my $opt_format = {
12
# Recording
13
attempts => [ 1, "attempts=n", 'Recording', '--attempts <number>', "Number of attempts to make or resume a failed connection. --attempts is applied per-stream, per-mode. Many modes have two or more streams available."],
14
- audioonly => [ 1, "audioonly|audio-only!", 'Recording', '--audio-only', "Only download audio stream for TV programme. 'hls' recording modes are not supported and ignored. Produces .m4a file. Implies --force."],
15
- downloadabortonfail => [ 1, "downloadabortonfail|download-abortonfail!", 'Recording', '--download-abortonfail', "Exit immediately if stream for any recording mode fails to download. Use to avoid repeated failed download attempts if connection is dropped or access is blocked."],
16
+ audioonly => [ 1, "audioonly|audio-only!", 'Recording', '--audio-only', "Only download audio stream for TV programme. Produces .m4a file. Implies --force."],
17
+ downloadabortonfail => [ 1, "downloadabortonfail||download-abort-onfail|download-abort-onfail!", 'Recording', '--download-abort-onfail', "Exit immediately if any stream to download. Use to avoid repeated failed download attempts if connection is dropped or access is blocked."],
18
+ excludeformat => [ 1, "excludeformat|exclude-format=s", 'Recording', '--exclude-format <format>,<format>,...', "Comma-separated list of media stream formats to ignore when recording. Valid values: hls,dash."],
19
excludesupplier => [ 1, "excludecdn|exclude-cdn|excludesupplier|exclude-supplier=s", 'Recording', '--exclude-supplier <supplier>,<supplier>,...', "Comma-separated list of media stream suppliers (CDNs) to skip. Possible values: akamai,limelight,bidi,cloudfront. Synonym: --exclude-cdn."],
20
force => [ 1, "force|force-download!", 'Recording', '--force', "Ignore programme history (unsets --hide option also)."],
21
- fps25 => [ 1, "fps25!", 'Recording', '--fps25', "Use only 25fps streams for TV programmes (HD video not available)."],
22
get => [ 2, "get|record|g!", 'Recording', '--get, -g', "Start recording matching programmes. Search terms required."],
23
+ includeformat => [ 1, "includeformat|include-format=s", 'Recording', '--include-format <format>,<format>,...', "Comma-separated list of media stream to use when recording. Overrides --exclude-format. Valid values: hls,dash"],
24
includesupplier => [ 1, "includecdn|include-cdn|includesupplier|include-supplier=s", 'Recording', '--include-supplier <supplier>,<supplier>,...', "Comma-separated list of media stream suppliers (CDNs) to use if not included by default or if previously excluded by --exclude-supplier. Possible values: akamai,limelight,bidi,cloudfront. Synonym: --include-cdn."],
25
hash => [ 1, "hash!", 'Recording', '--hash', "Show recording progress as hashes"],
26
logprogress => [ 1, "log-progress|logprogress!", 'Recording', '--log-progress', "Force HLS/DASH download progress display to be captured when screen output is redirected to file. Progress display is normally omitted unless writing to terminal."],
27
markdownloaded => [ 1, "markdownloaded|mark-downloaded!", 'Recording', '--mark-downloaded', "Mark programmes in search results or specified with --pid/--url as downloaded by inserting records in download history."],
28
- modes => [ 0, "modes=s", 'Recording', '--modes <mode>,<mode>,...', "Recording modes. See --tvmode and --radiomode (with --long-help) for available modes and defaults. Shortcuts: tvbest,tvbetter,tvgood,tvworst,radiobest,radiobetter,radiogood,radioworst (default=default for programme type)."],
29
+ modes => [ 0, "modes|quality=s", 'Recording', '--quality <quality>,<quality>,...', "TV and radio recording quality preference. See --tv-quality and --radio-quality for available values and defaults. Default: default for programme type."],
30
nomergeversions => [ 1, "nomergeversions|no-merge-versions!", 'Recording', '--no-merge-versions', "Do not merge programme versions with same name and duration."],
31
noproxy => [ 1, "noproxy|no-proxy!", 'Recording', '--no-proxy', "Ignore --proxy setting in preferences and/or http_proxy environment variable."],
32
overwrite => [ 1, "overwrite|over-write!", 'Recording', '--overwrite', "Overwrite recordings if they already exist"],
33
34
cacherebuild => [ 1, "rebuildcache|rebuild-cache|cacherebuild|cache-rebuild!", 'Config', '--cache-rebuild', "Rebuild cache with full 30-day programme index. Use --refresh-limit to restrict cache window."],
35
expiry => [ 1, "expiry|e=n", 'Config', '--expiry, -e <secs>', "Cache expiry in seconds (default 4hrs)"],
36
limitmatches => [ 1, "limitmatches|limit-matches=n", 'Config', '--limit-matches <number>', "Limits the number of matching results for any search (and for every PVR search)"],
37
- nopurge => [ 1, "no-purge|nopurge!", 'Config', '--nopurge', "Don't show warning about programmes recorded over 30 days ago"],
38
+ nopurge => [ 1, "no-purge|nopurge!", 'Hidden', '--nopurge', "Don't show warning about programmes recorded over 30 days ago"],
39
prefsadd => [ 0, "addprefs|add-prefs|prefsadd|prefs-add!", 'Config', '--prefs-add', "Add/Change specified saved user or preset options"],
40
prefsdel => [ 0, "del-prefs|delprefs|prefsdel|prefs-del!", 'Config', '--prefs-del', "Remove specified saved user or preset options"],
41
prefsclear => [ 0, "clear-prefs|clearprefs|prefsclear|prefs-clear!", 'Config', '--prefs-clear', "Remove *ALL* saved user or preset options"],
42
43
encodinglocalefs => [ 1, "encodinglocalefs|encoding-locale-fs=s", 'Misc', '--encoding-locale-fs <name>', "Character encoding used to encode file and directory names. Encoding name must be known to Perl Encode module. Default (only if auto-detect fails): Linux/Unix/OSX = UTF-8, Windows = cp1252"],
44
indexmaxconn => [ 1, "indexmaxconn|index-maxconn=n", 'Misc', '--index-maxconn <number>', "Maximum number of connections to use for concurrent programme indexing. Default: 5 Min: 1 Max: 10"],
45
noindexconcurrent => [ 1, "noindexconcurrent|no-index-concurrent!", 'Deprecated', '--no-index-concurrent', "Do not use concurrent indexing to update programme cache. Cache updates will be very slow."],
46
- purgefiles => [ 1, "purgefiles|purge-files!", 'Misc', '--purge-files', "Delete downloaded programmes more than 30 days old"],
47
releasecheck => [ 1, "releasecheck|release-check!", 'Misc', '--release-check', "Forces check for new release if used on command line. Checks for new release weekly if saved in preferences."],
48
throttle => [ 1, "bw|throttle=f", 'Misc', '--throttle <Mb/s>', "Bandwidth limit (in Mb/s) for media file download. Default: unlimited. Synonym: --bw"],
49
- trimhistory => [ 1, "trimhistory|trim-history=s", 'Misc', '--trim-history <# days to retain>', "Remove download history entries older than number of days specified in option value. Cannot specify 0 - use 'all' to completely delete download history"],
50
+ trimhistory => [ 1, "trimhistory|trim-history=s", 'Hidden', '--trim-history <# days to retain>', "Remove download history entries older than number of days specified in option value. Cannot specify 0 - use 'all' to completely delete download history"],
51
52
};
53
54
55
# Parse the cmdline using the opt_format hash
56
Options->usage( 0 ) if not $opt_cmdline->parse();
57
58
+# check obsolete options on command ilne
59
+my %nono_remap = (
60
+ "mode" => "quality",
61
+ "modes" => "quality",
62
+ "tvmode" => "tv-quality",
63
+ "radiomode" => "radio-quality",
64
+ "fps25" => "tv-lower-bitrate",
65
+);
66
+my $nono_regex = "(" . join("|", keys %nono_remap) . ")";
67
+my @nono_args = map { $_ =~ s/\-//g; $_ =~ s/=.*$//; $_;} grep(/--${nono_regex}/, @argv_save);
68
+my $nono_found;
69
+for my $nono_arg ( @nono_args ) {
70
+ $nono_found = 1;
71
+ logger "WARNING: --$nono_arg is deprecated and will be removed in a future release. Use --$nono_remap{$nono_arg}.\n";
72
+ # logger "ERROR: --$nono_arg is no longer supported. Use --$nono_remap{$nono_arg}.\n";
73
+}
74
+# exit 1 if $nono_found;
75
+
76
+# check invalid quality settings on command ilne
77
+my $regex_quality = {
78
+ tvmode => qr/(fhd|hd|sd|web|mobile|1080p?|720p?|540p?|396p?|288p?|default)/,
79
+ radiomode => qr/(high|std|med|low|320k?|128k?|96k?|48k?|default)/
80
+};
81
+$regex_quality->{modes} = "($regex_quality->{tvmode}|$regex_quality->{radiomode})";
82
+my $bad_quality;
83
+for my $qp ( "", "tv", "radio" ) {
84
+ my $qs = $qp ? "" : "s";
85
+ my $cmd_quality = "${qp}quality";
86
+ my $opt_quality = "${qp}mode${qs}";
87
+ my @bad_quality;
88
+ if ( $opt_cmdline->{$opt_quality} ) {
89
+ @bad_quality = grep(!/^$regex_quality->{$opt_quality}$/, split(/,/, $opt_cmdline->{$opt_quality}));
90
+ if ( @bad_quality ) {
91
+ $bad_quality = 1;
92
+ logger "WARNING: Invalid values in --${cmd_quality}/--${opt_quality}: '" . join(",", @bad_quality) . "'. This will be a fatal error in a future release.\n";
93
+ # logger "ERROR: Invalid values in --${cmd_quality}/--${opt_quality}: '" . join(",", @bad_quality) . "'\n";
94
+ }
95
+ }
96
+}
97
+# exit 1 if $bad_quality;
98
+
99
# process --start and --stop if necessary
100
foreach ('start', 'stop') {
101
if ($opt_cmdline->{$_} && $opt_cmdline->{$_} =~ /(\d\d):(\d\d)(:(\d\d))?/) {
102
103
# Sanitize preset file name
104
my $presetname = StringUtils::sanitize_path( $opt_cmdline->{preset}, 0, 1 );
105
$optfile_preset = File::Spec->catfile($presets_dir, $presetname);
106
- logger "INFO: Using user options preset '${presetname}'\n";
107
}
108
-logger "DEBUG: User preset options file: $optfile_preset\n" if defined $optfile_preset && $opt->{debug};
109
110
# Parse options if we're not saving/adding/deleting options (system-wide options are overridden by personal options)
111
if ( ! ( $opt_pre->{prefsadd} || $opt_pre->{prefsdel} || $opt_pre->{prefsclear} ) ) {
112
113
}
114
elsif ( $search_args[0] =~ m{^bbc-ipd:/*download/(\w+)/\w+/(\w+)/} ) {
115
$opt->{pid} = $1;
116
- $opt->{modes} ||= "best" if $2 eq "hd";
117
}
118
}
119
120
121
122
release_check();
123
my $retcode = 0;
124
-# Trim history
125
-if ( defined($opt->{trimhistory}) ) {
126
- my $hist = History->new();
127
- $hist->trim();
128
-# purge files
129
-} elsif ( $opt->{purgefiles} ) {
130
- my $hist = History->new();
131
- purge_downloaded_files( $hist, 30 );
132
# mark downloaded
133
-} elsif ( $opt->{markdownloaded} ) {
134
+if ( $opt->{markdownloaded} ) {
135
if ( ! $opt->{pid} && $no_search_args ) {
136
main::logger "ERROR: Search term(s) or --pid or --url required with --mark-downloaded\n";
137
exit 1;
138
139
my $hist = History->new();
140
my @pids = split( /,/, $opt->{pid} );
141
$retcode = download_pid_matches( $hist, find_pid_matches( $hist, @pids ) );
142
- purge_warning( $hist, 30 );
143
144
# Show history
145
} elsif ( $opt->{history} ) {
146
147
}
148
my $hist = History->new();
149
$retcode = download_matches( $hist, find_matches( $hist, @search_args ) );
150
- purge_warning( $hist, 30 );
151
}
152
exit $retcode;
153
154
155
}
156
157
# Generic
158
-# Checks history for files that are over 30 days old and asks user if they should be deleted
159
-# "$prog->{pid}|$prog->{name}|$prog->{episode}|$prog->{type}|".time()."|$prog->{mode}|$prog->{filename}\n";
160
-sub purge_downloaded_files {
161
- my $hist = shift;
162
- my @delete;
163
- my @proglist;
164
- my $days = shift;
165
-
166
- for my $pid ( $hist->get_pids() ) {
167
- my $record = $hist->get_record( $pid );
168
- if ( $record->{timeadded} < (time() - $days*86400) && $record->{filename} && -f $record->{filename} ) {
169
- # Calculate the seconds difference between epoch_now and epoch_datestring and convert back into array_time
170
- my @t = gmtime( time() - $record->{timeadded} );
171
- push @proglist, "$record->{name} - $record->{episode}, Recorded: $t[7] days $t[2] hours ago";
172
- push @delete, $record->{filename};
173
- }
174
- }
175
-
176
- if ( @delete ) {
177
- main::logger "\nThese programmes are over 30 days old and should be deleted:\n";
178
- main::logger "-----------------------------------\n";
179
- main::logger join "\n", @proglist;
180
- main::logger "\n-----------------------------------\n";
181
- main::logger "Do you wish to delete them now (Yes/No) ?\n";
182
- my $answer = <STDIN>;
183
- if ($answer =~ /^yes$/i ) {
184
- for ( @delete ) {
185
- main::logger "INFO: Deleting $_\n";
186
- unlink $_;
187
- }
188
- main::logger "Programmes deleted\n";
189
- } else {
190
- main::logger "No Programmes deleted\n";
191
- }
192
- }
193
-
194
- return 0;
195
-}
196
-
197
-sub purge_warning {
198
- my $hist = shift;
199
- my $days = shift;
200
- my $overdue;
201
- return 0 if $opt->{nopurge} || $opt->{nowrite};
202
- for my $pid ( $hist->get_pids() ) {
203
- my $record = $hist->get_record( $pid );
204
- if ( $record->{timeadded} < (time() - $days*86400) && $record->{filename} && -f $record->{filename} ) {
205
- $overdue = 1;
206
- last;
207
- }
208
- }
209
- if ( $overdue ) {
210
- print STDOUT "WARNING: You have programmes over 30 days old that should be deleted.\n";
211
- print STDOUT "WARNING: Find them with 'get_iplayer --history --before=720 \".*\"'\n";
212
- print STDOUT "WARNING: or use the 'Recordings' tab in the Web PVR Manager.\n";
213
- print STDOUT "WARNING: Use 'get_iplayer --purge-files' to delete all programmes over 30 days old.\n";
214
- print STDOUT "WARNING: Use 'get_iplayer --prefs-add --no-purge' to suppress this warning.\n";
215
- }
216
- return 0;
217
-}
218
-
219
# Returns url decoded string
220
sub url_decode {
221
my $str = shift;
222
223
'This applies even if the base option name already begins with "no-", e.g., --no-no-tag or --no-no-artwork',
224
);
225
push @man,
226
- '.TH GET_IPLAYER "1" "December 2021" "Phil Lewis" "get_iplayer Manual"',
227
+ '.TH GET_IPLAYER "1" "February 2022" "Phil Lewis" "get_iplayer Manual"',
228
'.SH NAME', 'get_iplayer - Stream Recording tool and PVR for BBC iPlayer and BBC Sounds',
229
'.SH SYNOPSIS',
230
'\fBget_iplayer\fR [<options>] [<regex|index> ...]',
231
232
233
# specify regex of options that cannot be saved
234
sub excludeopts {
235
- return '^(cache|profiledir|encoding|silent|help|debug|get|pvr|prefs|preset|warranty|conditions|dumpoptions|comment|purge|markdownloaded)';
236
+ return '^(cache|profiledir|encoding|silent|help|debug|get|pvr|prefs|preset|warranty|conditions|dumpoptions|comment|markdownloaded)';
237
}
238
239
# List all available presets in the specified dir
240
241
$History::optref = shift;
242
}
243
244
-sub trim {
245
- my $oldhistoryfile = "$historyfile.old";
246
- my $newhistoryfile = "$historyfile.new";
247
- if ( $opt->{trimhistory} =~ /^all$/i ) {
248
- if ( ! copy($historyfile, $oldhistoryfile) ) {
249
- die "ERROR: Cannot copy $historyfile to $oldhistoryfile: $!\n";
250
- }
251
- if ( ! unlink($historyfile) ) {
252
- die "ERROR: Cannot delete $historyfile: $! \n";
253
- }
254
- main::logger "INFO: Deleted all entries from download history\n";
255
- return;
256
- }
257
- if ( $opt->{trimhistory} !~ /^\d+$/ ) {
258
- die "ERROR: --trim-history option must have a positive integer value, or use 'all' to completely delete download history.\n";
259
- }
260
- if ( $opt->{trimhistory} =~ /^0+$/ ) {
261
- die "ERROR: Cannot specify 0 for --trim-history option. Use 'all' to completely delete download history.\n";
262
- }
263
- if ( ! open(HIST, "< $historyfile") ) {
264
- die "ERROR: Cannot read from $historyfile\n";
265
- }
266
- if ( ! open(NEWHIST, "> $newhistoryfile") ) {
267
- die "ERROR: Cannot write to $newhistoryfile\n";
268
- }
269
- my $trim_limit = time() - ($opt->{trimhistory} * 86400);
270
- my $deleted_count = 0;
271
- while (<HIST>) {
272
- chomp();
273
- next if /^[\#\s]/;
274
- my @record = split /\|/;
275
- my $timeadded = $record[4];
276
- if ( $timeadded >= $trim_limit ) {
277
- print NEWHIST "$_\n";
278
- } else {
279
- $deleted_count++;
280
- }
281
- }
282
- close HIST;
283
- close NEWHIST;
284
- if ( ! copy($historyfile, $oldhistoryfile) ) {
285
- die "ERROR: Cannot copy $historyfile to $oldhistoryfile: $!\n";
286
- }
287
- if ( ! move($newhistoryfile, $historyfile) ) {
288
- die "ERROR: Cannot move $newhistoryfile to $historyfile: $!\n";
289
- }
290
- main::logger "INFO: Deleted $deleted_count entries from download history\n";
291
-}
292
-
293
# Uses global @history_format
294
# Adds prog to history file (with a timestamp) so that it is not rerecorded after deletion
295
sub add {
296
297
# skip these
298
# If hash then list keys
299
} elsif ( ref$data{$_} eq 'HASH' ) {
300
+ next if ( $_ eq "modes" || $_ eq "modesizes") && ! $opt->{verbose};
301
for my $key ( sort keys %{$data{$_}} ) {
302
main::logger sprintf "%-16s ", $_.':';
303
if ( ref$data{$_}->{$key} ne 'HASH' ) {
304
- if ( $_ eq "modes" || $_ eq "modesizes" ) {
305
+ if ( $_ eq "modes" || $_ eq "modesizes" || $_ eq "qualities" || $_ eq "qualitysizes" ) {
306
my %seen = ();
307
- my @vals = sort Programme::cmp_modes grep { not $seen{$_}++ } map { $_ =~ s/([a-z])\d+/$1/; $_; } split(",", $data{$_}->{$key});
308
+ my @vals = sort Programme::cmp_modes grep { not $seen{$_}++ } map { $_ =~ s/([a-z])\d+/$1/; $_; } split(/,/, $data{$_}->{$key});
309
my $val = join(",", @vals);
310
main::logger "$key: $val";
311
- main::logger " [estimated sizes]" if $_ eq "modesizes";
312
+ main::logger " [estimated sizes]" if $_ eq "modesizes" || $_ eq "qualitysizes";
313
} else {
314
main::logger "$key: $data{$_}->{$key}";
315
}
316
317
sub cmp_modes($$) {
318
my ($x, $y) = @_;
319
my %ranks = (
320
- 'hd(\d+)?' => 1000,
321
- '[^x]sd(\d+)?' => 2000,
322
- 'xsd(\d+)?' => 3000,
323
- '[^vx]high(\d+)?' => 4000,
324
- 'xhigh(\d+)?' => 5000,
325
- '[^x]std(\d+)?' => 6000,
326
- 'xstd(\d+)?' => 7000,
327
- 'med(\d+)?' => 8000,
328
- 'low(\d+)?' => 9000,
329
- 'subtitles(\d+)?' => 10000,
330
+ 'fhd(\d+)?' => 1000,
331
+ '(\b|[^f])hd(\d+)?' => 2000,
332
+ '(\b|[^x])sd(\d+)?' => 3000,
333
+ 'xsd(\d+)?' => 4000,
334
+ '(\b|[^x])web(\d+)?' => 5000,
335
+ 'xweb(\d+)?' => 6000,
336
+ 'mobile(\d+)?' => 7000,
337
+ 'high(\d+)?' => 8000,
338
+ 'std(\d+)?' => 9000,
339
+ 'med(\d+)?' => 10000,
340
+ 'low(\d+)?' => 11000,
341
+ 'subtitles(\d+)?' => 12000,
342
);
343
my %ranks2 = (
344
- '^haf' => 10,
345
- '^hla' => 20,
346
- '^hvf' => 30,
347
- '^hls' => 40,
348
- '^daf' => 50,
349
- '^dvf' => 60,
350
- '^subtitle' => 70,
351
+ '^hls' => 10,
352
+ '^dash' => 20,
353
+ '^subtitle' => 30,
354
);
355
my ($rank_x, $rank_y);
356
for my $k ( keys %ranks ) {
357
358
main::logger "WARNING: A UK TV licence is required to access BBC iPlayer TV content legally\n";
359
}
360
361
- # Get all possible (or user overridden) modes for this prog recording
362
- my $modelist = $prog->modelist();
363
- main::logger "INFO: Mode list: $modelist\n" if $opt->{verbose};
364
-
365
######## version loop #######
366
# Do this for each version tried in this order (if they appeared in the content)
367
for my $version ( @version_search_list ) {
368
369
main::logger "INFO: Regenerate filename for version change: $prog->{version} -> $version\n" if ( $prog->{version} && $opt->{verbose} );
370
}
371
$prog->{version} = $version;
372
+ $prog->{verpid} = $prog->{verpids}->{$version};
373
main::logger "INFO: Found version: '$prog->{version}'\n" if $opt->{verbose};
374
375
# Try to get stream data for this version if not already populated
376
377
$prog->{streams}->{$version} = $prog->get_stream_data( $prog->{verpids}->{$version}, undef, $version );
378
}
379
380
+ # Get all possible (or user overridden) modes for this prog version
381
+ my $modelist = $prog->modelist();
382
+ main::logger "INFO: Mode list for version '$prog->{version}': $modelist\n" if $opt->{verbose};
383
+
384
########## mode loop ########
385
# record prog depending on the prog type
386
387
388
my @modes;
389
my @available_modes = sort keys %{ $prog->{streams}->{$version} };
390
for my $modename ( split /,/, $modelist ) {
391
- next if $opt->{audioonly} && $prog->{type} eq "tv" && $modename =~ /^hls/;
392
+ # next if $opt->{audioonly} && $prog->{type} eq "tv" && $modename =~ /^hls/;
393
# find all numbered modes starting with this modename
394
push @modes, sort { $a cmp $b } grep /^$modename(\d+)?$/, @available_modes;
395
}
396
397
my %available_modes_short;
398
# Strip the number from the end of the mode name and make a unique array
399
for my $modename ( @available_modes ) {
400
- next if $opt->{audioonly} && $prog->{type} eq "tv" && $modename =~ /^hls/;
401
+ # next if $opt->{audioonly} && $prog->{type} eq "tv" && $modename =~ /^hls/;
402
next if $modename =~ /subtitle/;
403
$modename =~ s/\d+$//g;
404
$available_modes_short{$modename}++;
405
}
406
- my $msg = "No supported modes";
407
+ my $msg = "No supported recording quality";
408
if ( $opt->{$prog->{type}."mode"} || $opt->{modes} ) {
409
- $msg = "No specified modes";
410
+ $msg = "No specified recording quality";
411
}
412
+ my $available_qualities = $prog->qualities_from_modes( join( ",", sort Programme::cmp_modes @available_modes ) );
413
main::logger "INFO: $msg ".($modelist ? "($modelist) " : "")."available for this programme with version '$version'\n";
414
- if ( keys %available_modes_short ) {
415
- main::logger "INFO: Available modes: ".(join ',', sort Programme::cmp_modes keys %available_modes_short)."\n";
416
+ if ( $available_qualities ) {
417
+ main::logger "INFO: Available qualities: $available_qualities\n"
418
} else {
419
- main::logger "INFO: No other modes are available\n";
420
+ main::logger "INFO: No other recording quality is available\n";
421
main::logger "INFO: The programme may no longer be available - check the iPlayer or Sounds site\n";
422
main::logger "INFO: The programme may only be available in an unsupported format (e.g., Flash) - check the iPlayer or Sounds site\n";
423
main::logger "INFO: If you use a VPN/VPS/Smart DNS/web proxy, it may have been blocked\n";
424
425
$prog->{mode} = $mode;
426
# Keep short mode name for substitutions
427
$prog->{modeshort} = $modeshort;
428
+ $prog->{quality} = $prog->qualities_from_modes($mode);
429
430
# try the recording for this mode (rtn==0 -> success, rtn==1 -> next mode, rtn==2 -> next prog)
431
$retcode = mode_ver_download_retry_loop( $prog, $hist, $ua, $mode, $version, $prog->{verpids}->{$version} );
432
433
$prog->{streams}->{$version} = $prog->get_stream_data( $version_pid, undef, $version );
434
if ( keys %{ $prog->{streams}->{$version} } == 0 ) {
435
main::logger "WARNING: No streams available for '$version' version ($prog->{verpids}->{$version}) - skipping (retry)\n";
436
- if ( $prog->{geoblocked} ) {
437
+ if ( $prog->{geoblocked}->{$version} ) {
438
main::logger "WARNING: The BBC blocked access to this programme because it determined that you are outside the UK. (retry)\n";
439
- } elsif ( $prog->{unavailable} ) {
440
+ } elsif ( $prog->{unavailable}->{$version} ) {
441
main::logger "WARNING: The BBC lists this programme as unavailable - check the iPlayer or Sounds site (retry)\n";
442
}
443
return 2;
444
445
$template->{generic} .= '<program_meta_data xmlns="http://linuxcentre.net/xmlstuff/get_iplayer" revision="1">'."\n";
446
my $version = $prog->{version} || 'unknown';
447
for my $key ( sort keys %{$prog} ) {
448
+ next if $key eq "versions";
449
my $subst = "[$key]";
450
my $value = $prog->{$key};
451
- if ( ref$value eq 'HASH' ) {
452
- if ( ref$value->{$version} ne 'HASH' ) {
453
- # abbreviate mode lists
454
- if ( $key eq "modes" || $key eq "modesizes" ) {
455
- my %seen = ();
456
- my @vals = sort Programme::cmp_modes grep { not $seen{$_}++ } map { $_ =~ s/([a-z])\d+/$1/; $_; } split(",", $value->{$version});
457
- $subst = join(",", @vals);
458
- }
459
- }
460
- }
461
+ next if ( ref$value eq 'HASH' );
462
$template->{generic} .= "\t<$key>$subst</$key>\n";
463
}
464
$template->{generic} .= "</program_meta_data>\n";
465
466
$self->{runtime} = int($self->{duration} / 60);
467
my $jom = {};
468
for my $key ( sort keys %{$self} ) {
469
+ next if $key eq "versions";
470
my $value = $self->{$key};
471
- # Get version specific value if this key is a hash
472
- if ( ref$value eq 'HASH' ) {
473
- if ( ref$value->{$version} ne 'HASH' ) {
474
- # abbreviate mode lists
475
- if ( $key eq "modes" || $key eq "modesizes" ) {
476
- my %seen = ();
477
- my @vals = sort Programme::cmp_modes grep { not $seen{$_}++ } map { $_ =~ s/([a-z])\d+/$1/; $_; } split(",", $value->{$version});
478
- $value = join(",", @vals);
479
- } else {
480
- $value = $value->{$version};
481
- }
482
- } else {
483
- next;
484
- }
485
- }
486
+ next if ( ref$value eq 'HASH' );
487
# Join array elements if value is ARRAY type
488
if ( ref$value eq 'ARRAY' ) {
489
$value = join ',', @{ $value };
490
491
$prog->{ext} = 'EXT' if ! $prog->{ext};
492
# output files with --raw
493
if ( $opt->{raw} && $mode ) {
494
- if ( $mode =~ /(haf|hvf|hls)/ ) {
495
+ if ( $mode =~ /^hls/ ) {
496
$prog->{ext} = "ts";
497
- } elsif ( $mode =~ /daf/ ) {
498
+ } elsif ( $mode =~ /^dash/ && $prog->{type} eq "radio" ) {
499
$prog->{ext} = "raw.m4a";
500
- } elsif ( $mode =~ /dvf/ ) {
501
+ } elsif ( $mode =~ /^dash/ && $prog->{type} eq "tv" ) {
502
$prog->{rawaudio} = main::encode_fs(File::Spec->catfile($prog->{dir}, "$prog->{fileprefix}.raw.m4a"));
503
$prog->{rawvideo} = main::encode_fs(File::Spec->catfile($prog->{dir}, "$prog->{fileprefix}.raw.m4v"));
504
}
505
506
507
my $modes;
508
my $mode_sizes;
509
+ my $qualities;
510
+ my $quality_sizes;
511
for my $version ( sort keys %{ $prog->{verpids} } ) {
512
my @version_modes = sort Programme::cmp_modes keys %{ $prog->{streams}->{$version} };
513
$modes->{$version} = join ',', @version_modes;
514
+ $qualities->{$version} = $prog->qualities_from_modes($modes->{$version});
515
# Estimate the file sizes for each mode
516
my @sizes;
517
for my $mode ( @version_modes ) {
518
519
}
520
}
521
$mode_sizes->{$version} = join ',', sort Programme::cmp_modes @sizes;
522
+ $quality_sizes->{$version} = $prog->qualitysizes_from_modesizes($mode_sizes->{$version});
523
}
524
525
$prog->{versions} = $versions;
526
$prog->{modes} = $modes;
527
$prog->{modesizes} = $mode_sizes;
528
+ $prog->{qualities} = $qualities;
529
+ $prog->{qualitysizes} = $quality_sizes;
530
531
# check at least one version available
532
if ( keys %{ $prog->{verpids} } == 0 ) {
533
main::logger "WARNING: No media streams found for requested programme versions and recording modes.\n";
534
- if ( $prog->{geoblocked} ) {
535
+ if ( $prog->{geoblocked}->{$version} ) {
536
main::logger "WARNING: The BBC blocked access to this programme because it determined that you are outside the UK.\n";
537
- } elsif ( $prog->{unavailable} ) {
538
+ } elsif ( $prog->{unavailable}->{$version} ) {
539
main::logger "WARNING: The BBC lists this programme as unavailable - check the iPlayer or Sounds site.\n";
540
} else {
541
main::logger "WARNING: The programme may no longer be available - check the iPlayer or Sounds site.\n";
542
543
}
544
$conn->{href} = $manifest_url;
545
my $xml = main::request_url_retry( $ua, $conn->{href}, 3, undef, undef, 1, undef, 1 );
546
- if ( ! $xml ) {
547
+ if ( $xml !~ /<\?xml.*?<MPD/si ) {
548
main::logger "WARNING: No DASH manifest returned ($conn->{href})\n" if $opt->{verbose};
549
return;
550
}
551
my $dom;
552
eval { $dom = XML::LibXML->load_xml(string => $xml); };
553
if ( $@ ) {
554
- main::logger "ERROR: Failed to load DASH manifest:\n$@";
555
+ main::logger "ERROR: Failed to load DASH manifest:\n$@" if $opt->{verbose};
556
return;
557
}
558
my $xpc = XML::LibXML::XPathContext->new($dom);
559
560
# $media = undef|<modename>
561
sub get_stream_data {
562
my ( $prog, $verpid, $media, $version ) = @_;
563
- my $modelist = $prog->modelist();
564
my $data = {};
565
566
main::logger "INFO: Getting stream data for version: '$version'\n" if $opt->{verbose};
567
568
$exclude_regex = '_('.(join('|', @exclude_supplier)).')';
569
}
570
571
+ # filter formats
572
+ my @exclude_format = split(/,/, $opt->{excludeformat});
573
+ if ( $opt->{includeformat} ) {
574
+ @exclude_format = grep { $opt->{includeformat} !~ /\b$_\b/ } @exclude_format;
575
+ }
576
+ my $exclude_format;
577
+ if ( @exclude_format ) {
578
+ $exclude_format = join(',', @exclude_format);
579
+ }
580
+
581
# retrieve stream data
582
my $ua = main::create_ua( 'desktop' );
583
my $unblocked;
584
585
my @medias;
586
my @mediasets;
587
my @ms_tf;
588
- my $unknown_modes = $modelist !~ /(daf|dvf|haf|hla|hvf)/;
589
- my $get_dash = $opt->{info} || $modelist =~ /(daf|dvf)/ || $unknown_modes;
590
- my $get_hls = $opt->{info} || $modelist =~ /(haf|hla|hvf)/ || $unknown_modes;
591
+ my $get_dash = $opt->{info} || $exclude_format !~ /dash/;
592
+ my $get_hls = $opt->{info} || $exclude_format !~ /hls/;
593
if ( $get_dash ) {
594
push @ms_tf, "dash";
595
push @mediasets, "iptv-all", "pc";
596
597
}
598
599
unless ( $unblocked ) {
600
- $prog->{geoblocked} = 1 if $checked_geoblock;
601
+ $prog->{geoblocked}->{$version} = 1 if $checked_geoblock;
602
return undef;
603
}
604
605
unless ( $isavailable ) {
606
- $prog->{unavailable} = 1 if $checked_unavailable;
607
+ $prog->{unavailable}->{$version} = 1 if $checked_unavailable;
608
return undef;
609
}
610
611
612
613
# Put verpid into mattribs
614
$mattribs->{verpid} = $verpid;
615
- $mattribs->{modelist} = $modelist;
616
617
- if ( $mattribs->{service} =~ /hla/ ) {
618
- if ( $mattribs->{kind} =~ 'audio' ) {
619
- my $ext = "m4a";
620
- if ( $mattribs->{bitrate} >= 192 ) {
621
- get_stream_data_cdn( $data, $mattribs, 'hlahigh', 'hls', $ext );
622
- } elsif ( $mattribs->{bitrate} >= 120 ) {
623
- get_stream_data_cdn( $data, $mattribs, 'hlastd', 'hls', $ext );
624
- } elsif ( $mattribs->{bitrate} >= 80 ) {
625
- get_stream_data_cdn( $data, $mattribs, 'hlamed', 'hls', $ext );
626
- } else {
627
- get_stream_data_cdn( $data, $mattribs, 'hlalow', 'hls', $ext );
628
- }
629
+ if ( $mattribs->{service} =~ /(daf|haf|hla)/ && $mattribs->{kind} =~ 'audio' ) {
630
+ my $stm = $mattribs->{service} =~ /daf/ ? "dash" : "hls";
631
+ my $ext = "m4a";
632
+ if ( $mattribs->{bitrate} >= 192 ) {
633
+ get_stream_data_cdn( $data, $mattribs, "${stm}high", $stm, $ext );
634
+ } elsif ( $mattribs->{bitrate} >= 120 ) {
635
+ get_stream_data_cdn( $data, $mattribs, "${stm}std", $stm, $ext );
636
+ } elsif ( $mattribs->{bitrate} >= 80 ) {
637
+ get_stream_data_cdn( $data, $mattribs, "${stm}med", $stm, $ext );
638
+ } else {
639
+ get_stream_data_cdn( $data, $mattribs, "${stm}low", $stm, $ext );
640
}
641
642
- } elsif ( $mattribs->{service} =~ /hvf/ ) {
643
- if ( $mattribs->{kind} =~ 'video' ) {
644
- my $ext = "mp4";
645
- if ( $mattribs->{height} > 1000 ) {
646
- # full HD streams do not exist yet
647
- } elsif ( $mattribs->{height} > 700 ) {
648
- get_stream_data_cdn( $data, $mattribs, "hvfhd", 'hls', $ext );
649
- } elsif ( $mattribs->{height} > 500 ) {
650
- if ( $mattribs->{fps} > 25 ) {
651
- get_stream_data_cdn( $data, $mattribs, "hvfsd", 'hls', $ext );
652
+ } elsif ( $mattribs->{service} =~ /(dvf|hvf|hls)/ && $mattribs->{kind} =~ 'video' ) {
653
+ my $stm = $mattribs->{service} =~ /dvf/ ? "dash" : "hls";
654
+ my $ext = "mp4";
655
+ if ( $mattribs->{height} > 700 ) {
656
+ get_stream_data_cdn( $data, $mattribs, "${stm}hd", $stm, $ext );
657
+ } elsif ( $mattribs->{height} > 500 ) {
658
+ if ( $mattribs->{fps} > 25 ) {
659
+ if ( $mattribs->{bitrate} >= 2500 ) {
660
+ get_stream_data_cdn( $data, $mattribs, "${stm}sd", $stm, $ext );
661
} else {
662
- get_stream_data_cdn( $data, $mattribs, "hvfxsd", 'hls', $ext );
663
+ get_stream_data_cdn( $data, $mattribs, "${stm}xsd", $stm, $ext );
664
}
665
- } elsif ( $mattribs->{height} > 360 ) {
666
- if ( $mattribs->{fps} > 25 ) {
667
- get_stream_data_cdn( $data, $mattribs, "hvfhigh", 'hls', $ext );
668
- } else {
669
- get_stream_data_cdn( $data, $mattribs, "hvfxhigh", 'hls', $ext );
670
- }
671
- } elsif ( $mattribs->{height} > 260 && $mattribs->{height} < 300 ) {
672
- get_stream_data_cdn( $data, $mattribs, "hvflow", 'hls', $ext );
673
- }
674
- }
675
-
676
- } elsif ( $mattribs->{service} =~ /haf/ ) {
677
- if ( $mattribs->{kind} =~ 'audio' ) {
678
- my $ext = "m4a";
679
- if ( $mattribs->{bitrate} >= 192 ) {
680
- get_stream_data_cdn( $data, $mattribs, 'hafhigh', 'hls', $ext );
681
- } elsif ( $mattribs->{bitrate} >= 120 ) {
682
- get_stream_data_cdn( $data, $mattribs, 'hafstd', 'hls', $ext );
683
- } elsif ( $mattribs->{bitrate} >= 80 ) {
684
- get_stream_data_cdn( $data, $mattribs, 'hafmed', 'hls', $ext );
685
} else {
686
- get_stream_data_cdn( $data, $mattribs, 'haflow', 'hls', $ext );
687
+ get_stream_data_cdn( $data, $mattribs, "${stm}xsd", $stm, $ext );
688
}
689
- }
690
-
691
- } elsif ( $mattribs->{service} =~ /dvf/ ) {
692
- if ( $mattribs->{kind} =~ 'video' ) {
693
- my $ext = "mp4";
694
- if ( $mattribs->{height} > 700 ) {
695
- get_stream_data_cdn( $data, $mattribs, "dvfhd", 'dash', $ext );
696
- } elsif ( $mattribs->{height} > 500 ) {
697
- if ( $mattribs->{bitrate} > 2500 ) {
698
- get_stream_data_cdn( $data, $mattribs, "dvfsd", 'dash', $ext );
699
+ } elsif ( $mattribs->{height} > 350 ) {
700
+ if ( $mattribs->{fps} > 25 ) {
701
+ if ( $mattribs->{bitrate} >= 1250 ) {
702
+ get_stream_data_cdn( $data, $mattribs, "${stm}web", $stm, $ext );
703
} else {
704
- get_stream_data_cdn( $data, $mattribs, "dvfxsd", 'dash', $ext );
705
+ get_stream_data_cdn( $data, $mattribs, "${stm}xweb", $stm, $ext );
706
}
707
- } elsif ( $mattribs->{height} > 350 ) {
708
- if ( $mattribs->{bitrate} > 1500 ) {
709
- get_stream_data_cdn( $data, $mattribs, "dvfhigh", 'dash', $ext );
710
- } else {
711
- get_stream_data_cdn( $data, $mattribs, "dvfxhigh", 'dash', $ext );
712
- }
713
- } elsif ( $mattribs->{height} > 250 ) {
714
- get_stream_data_cdn( $data, $mattribs, "dvflow", 'dash', $ext );
715
- }
716
- }
717
-
718
- } elsif ( $mattribs->{service} =~ /daf/ ) {
719
- if ( $mattribs->{kind} =~ 'audio' ) {
720
- my $ext = "m4a";
721
- # use DASH 320k stream as HLS 320k stream
722
- if ( $mattribs->{bitrate} >= 192 ) {
723
- get_stream_data_cdn( $data, $mattribs, "dafhigh", 'dash', $ext );
724
- } elsif ( $mattribs->{bitrate} >= 120 ) {
725
- get_stream_data_cdn( $data, $mattribs, "dafstd", 'dash', $ext );
726
- } elsif ( $mattribs->{bitrate} >= 80 ) {
727
- get_stream_data_cdn( $data, $mattribs, "dafmed", 'dash', $ext );
728
} else {
729
- get_stream_data_cdn( $data, $mattribs, "daflow", 'dash', $ext );
730
+ get_stream_data_cdn( $data, $mattribs, "${stm}xweb", $stm, $ext );
731
}
732
+ } elsif ( $mattribs->{height} > 250 ) {
733
+ get_stream_data_cdn( $data, $mattribs, "${stm}mobile", $stm, $ext );
734
}
735
736
# Subtitles modes
737
738
}
739
}
740
741
+ # generate FHD streams
742
+ for my $key (keys %{$data}) {
743
+ if ( $key =~ m/(hls|dash)hd/ ) {
744
+ my $xvi = 12000;
745
+ my $xvb = 8490;
746
+ my $xvw = 1920;
747
+ my $xvh = 1080;
748
+ my $xvr = 50;
749
+ my $stm2 = dclone($data->{$key});
750
+ (my $key2 = $key) =~ s/(hls|dash)hd/${1}fhd/;
751
+ $stm2->{streamurl} =~ s/video=\d+/video=${xvi}000/;
752
+ $stm2->{video_bitrate} = $xvb;
753
+ $stm2->{bitrate} = $stm2->{video_bitrate} + $stm2->{audio_bitrate};
754
+ $stm2->{type} =~ s/^(.*?gip_.vf_)\d+(.*?)\d+x\d+(.*?)\d+(fps.*?)\d+(kbps.*)$/$1${xvb}$2${xvw}x${xvh}$3${xvr}$4${xvb}$5/;
755
+ $stm2->{type} = sprintf("%12s %4s %4s %9s %5s %8s %7s %s", split(" ", $stm2->{type}));
756
+ if ( $key2 =~ /dashfhd/ ) {
757
+ $stm2->{size} = int($stm2->{audio_media}->{programme_duration} * $stm2->{bitrate} * 1000.0 / 8.0);
758
+ $stm2->{video_media}->{height} = $xvh;
759
+ $stm2->{video_media}->{width} = $xvw;
760
+ $stm2->{video_media}->{bitrate} = $xvb;
761
+ $stm2->{video_media}->{file_size} = int($stm2->{video_media}->{programme_duration} * $stm2->{video_media}->{bitrate} * 1000.0 / 8.0);
762
+ $stm2->{video_media}->{service} =~ s/(gip_dvf_)\d+/$1${xvb}/;
763
+ $stm2->{video_media}->{id} = "video=${xvi}000";
764
+ }
765
+ my $xvs = main::request_url_retry( $ua, $stm2->{streamurl}, 3, undef, undef, 1, undef, 1 );
766
+ if ( $xvs && $xvs !~ /<html/i ) {
767
+ $data->{$key2} = $stm2;
768
+ }
769
+ }
770
+ }
771
+
772
# Report modes found
773
if ( $opt->{verbose} ) {
774
main::logger sprintf("INFO: Found mode %10s: %s\n", $_, $data->{$_}->{type}) for sort Programme::cmp_modes keys %{ $data };
775
776
777
sub modelist {
778
my $prog = shift;
779
+ my $regex_quality = qr/(fhd|hd|sd|web|mobile|high|std|med|low|default)/;
780
+ my $regex_alias = qr/(1080p?|720p?|540p?|396p?|288p?|320k?|128k?|96k?|48k?)/;
781
+ my $replace_alias = {
782
+ "1080p" => "fhd",
783
+ "720p" => "hd",
784
+ "540p" => "sd",
785
+ "396p" => "web",
786
+ "288p" => "mobile",
787
+ "320k" => "high",
788
+ "128k" => "std",
789
+ "96k" => "med",
790
+ "48k" => "low",
791
+ "1080" => "fhd",
792
+ "720" => "hd",
793
+ "540" => "sd",
794
+ "396" => "web",
795
+ "288" => "mobile",
796
+ "320" => "high",
797
+ "128" => "std",
798
+ "96" => "med",
799
+ "48" => "low",
800
+ };
801
+ my $tvlbr = $opt->{fps25};
802
my $mlist = $opt->{$prog->{type}."mode"} || $opt->{modes};
803
- # Defaults
804
- if ( ! $mlist ) {
805
- $mlist = 'default';
806
- }
807
+ $mlist =~ s/\s+//g;
808
+ $mlist =~ s/,{2,}/,/g;
809
+ $mlist = lc($mlist);
810
my $mlist_orig = $mlist;
811
# backcompat
812
- $mlist =~ s/(\b|[^t])vgood/$1better/g;
813
+ # prefixes first
814
+ $mlist =~ s/(flash|hls)aac//g;
815
+ $mlist =~ s/(flash|rtmp)//g;
816
+ $mlist =~ s/(dash|dvf|daf)//g;
817
+ $mlist =~ s/(hls|hvf|haf|hla)//g;
818
+ $mlist =~ s/(tv|radio)//g;
819
+ # then suffixes
820
+ $mlist =~ s/([^,\d]+)\d+/$1/g;
821
+ $mlist =~ s/vgood/better/g;
822
$mlist =~ s/worse/good/g;
823
- $mlist =~ s/hlsvhigh/hvfxsd/g;
824
- $mlist =~ s/(hlsx?|hvf)std/hvfxhigh/g;
825
- $mlist =~ s/(flash|hls)aac/radio/g;
826
- $mlist =~ s/(flash|rtmp)/tv/g;
827
- if ( $mlist ne $mlist_orig && ! $opt->{nowarnmoderemap} ) {
828
- main::logger "WARNING: Invalid mode list '$mlist_orig' remapped to '$mlist'\n";
829
- main::logger "WARNING: Please update your preferences\n";
830
- $opt->{nowarnmoderemap} = 1;
831
+ $mlist =~ s/vhigh/sd/g;
832
+ $mlist =~ s/x(high|std)/xweb/g;
833
+ if ( $prog->{type} eq "tv" ) {
834
+ $mlist =~ s/high/web/g;
835
+ $mlist =~ s/low/mobile/g;
836
}
837
- # stream format aliases
838
+ # then expansions
839
if ( $prog->{type} eq "tv" ) {
840
- $mlist =~ s/dash/dvf/g;
841
- $mlist =~ s/hls(?!hd)/hvf/g;
842
+ $mlist =~ s/best/hd,sd,web,mobile/g;
843
+ $mlist =~ s/better/sd,web,mobile/g;
844
+ $mlist =~ s/good/web,mobile/g;
845
+ $mlist =~ s/worst/mobile/g;
846
} elsif ( $prog->{type} eq "radio" ) {
847
- $mlist =~ s/dash/daf/g;
848
- $mlist =~ s/hls(?!hd)/hlsaudio/g;
849
+ $mlist =~ s/best/high,std,med,low/g;
850
+ $mlist =~ s/better/std,med,low/g;
851
+ $mlist =~ s/good/med,low/g;
852
+ $mlist =~ s/worst/low/g;
853
}
854
- # Deal with fallback modes and expansions
855
- # Generic aliases
856
- $mlist = main::expand_list($mlist, 'default', "$prog->{type}default");
857
- $mlist = main::expand_list($mlist, 'best', "$prog->{type}best");
858
- $mlist = main::expand_list($mlist, 'better', "$prog->{type}better");
859
- $mlist = main::expand_list($mlist, 'good', "$prog->{type}good");
860
- $mlist = main::expand_list($mlist, 'worst', "$prog->{type}worst");
861
- # single quality levels
862
- if ( $prog->{type} eq "tv" ) {
863
- $mlist = main::expand_list($mlist, 'hd', "$prog->{type}hd");
864
- $mlist = main::expand_list($mlist, 'sd', "$prog->{type}sd");
865
+ $mlist = join( ",", grep( /^(${regex_quality}|${regex_alias})$/, split( /,/, $mlist ) ) );
866
+ if ( $mlist ne $mlist_orig && ! $opt->{nowarnmoderemap} ) {
867
+ main::logger "WARNING: Recording quality settings '$mlist_orig' corrected to '$mlist'\n";
868
+ main::logger "WARNING: Please update your preferences, preset, or PVR search\n";
869
+ $opt->{nowarnmoderemap} = 1;
870
}
871
- $mlist = main::expand_list($mlist, 'high', "$prog->{type}high");
872
- if ( $prog->{type} eq "radio" ) {
873
- $mlist = main::expand_list($mlist, 'std', "$prog->{type}std");
874
- $mlist = main::expand_list($mlist, 'med', "$prog->{type}med");
875
- }
876
- $mlist = main::expand_list($mlist, 'low', "$prog->{type}low");
877
- # DASH on-demand radio
878
- if ( $prog->{type} eq "radio" && $mlist =~ /daf/ ) {
879
- $mlist = main::expand_list($mlist, 'daf', 'dafdefault');
880
- $mlist = main::expand_list($mlist, 'dafdefault', 'dafbest');
881
- $mlist = main::expand_list($mlist, 'dafbest', 'dafhigh,dafbetter');
882
- $mlist = main::expand_list($mlist, 'dafbetter', 'dafstd,dafgood');
883
- $mlist = main::expand_list($mlist, 'dafgood', 'dafmed,dafworst');
884
- $mlist = main::expand_list($mlist, 'dafworst', 'daflow');
885
- }
886
- # DASH on-demand tv
887
- if ( $prog->{type} eq "tv" && $mlist =~ /dvf/ ) {
888
- $mlist = main::expand_list($mlist, 'dvf', 'dvfdefault');
889
- $mlist = main::expand_list($mlist, 'dvfdefault', 'dvfbest');
890
- if ( $opt->{fps25} ) {
891
- $mlist = main::expand_list($mlist, 'dvfbest', 'dvfbetter');
892
- $mlist = main::expand_list($mlist, 'dvfbetter', 'dvfxsd,dvfgood');
893
- $mlist = main::expand_list($mlist, 'dvfgood', 'dvfxhigh,dvfworst');
894
- } else {
895
- $mlist = main::expand_list($mlist, 'dvfbest', 'dvfhd,dvfbetter');
896
- $mlist = main::expand_list($mlist, 'dvfbetter', 'dvfsd,dvfxsd,dvfgood');
897
- $mlist = main::expand_list($mlist, 'dvfgood', 'dvfhigh,dvfxhigh,dvfworst');
898
- }
899
- $mlist = main::expand_list($mlist, 'dvfworst', 'dvflow');
900
- }
901
- # HLS Audio Factory on-demand radio
902
- if ( $prog->{type} eq "radio" && $mlist =~ /haf/ ) {
903
- $mlist = main::expand_list($mlist, 'haf', 'hafdefault');
904
- $mlist = main::expand_list($mlist, 'hafdefault', 'hafbest');
905
- $mlist = main::expand_list($mlist, 'hafbest', 'hafhigh,hafbetter');
906
- $mlist = main::expand_list($mlist, 'hafbetter', 'hafstd,hafgood');
907
- $mlist = main::expand_list($mlist, 'hafgood', 'hafmed,hafworst');
908
- $mlist = main::expand_list($mlist, 'hafworst', 'haflow');
909
- }
910
- # HLS audio clips and archives
911
- if ( $prog->{type} eq "radio" && $mlist =~ /hla/ ) {
912
- $mlist = main::expand_list($mlist, 'hla', 'hladefault');
913
- $mlist = main::expand_list($mlist, 'hladefault', 'hlabest');
914
- $mlist = main::expand_list($mlist, 'hlabest', 'hlahigh,hlabetter');
915
- $mlist = main::expand_list($mlist, 'hlabetter', 'hlastd,hlagood');
916
- $mlist = main::expand_list($mlist, 'hlagood', 'hlamed,hlaworst');
917
- $mlist = main::expand_list($mlist, 'hlaworst', 'hlalow');
918
- }
919
- # HLS Video Factory on-demand tv
920
- if ( $prog->{type} eq "tv" && $mlist =~ /hvf/ ) {
921
- $mlist = main::expand_list($mlist, 'hvf', 'hvfdefault');
922
- $mlist = main::expand_list($mlist, 'hvfdefault', 'hvfbest');
923
- if ( $opt->{fps25} ) {
924
- $mlist = main::expand_list($mlist, 'hvfbest', 'hvfbetter');
925
- $mlist = main::expand_list($mlist, 'hvfbetter', 'hvfxsd,hvfgood');
926
- $mlist = main::expand_list($mlist, 'hvfgood', 'hvfxhigh,hvfworst');
927
- } else {
928
- $mlist = main::expand_list($mlist, 'hvfbest', 'hvfhd,hvfbetter');
929
- $mlist = main::expand_list($mlist, 'hvfbetter', 'hvfsd,hvfxsd,hvfgood');
930
- $mlist = main::expand_list($mlist, 'hvfgood', 'hvfhigh,hvfxhigh,hvfworst');
931
- }
932
- $mlist = main::expand_list($mlist, 'hvfworst', 'hvflow');
933
- }
934
- # HLS on-demand radio
935
- if ( $prog->{type} eq "radio" && $mlist =~ /hlsaudio/ ) {
936
- $mlist = main::expand_list($mlist, 'hlsaudio', 'hlsaudiodefault');
937
- $mlist = main::expand_list($mlist, 'hlsaudiodefault', 'hlsaudiobest');
938
- $mlist = main::expand_list($mlist, 'hlsaudiobest', 'hafhigh,hlahigh,hlsaudiobetter');
939
- $mlist = main::expand_list($mlist, 'hlsaudiobetter', 'hafstd,hlastd,hlsaudiogood');
940
- $mlist = main::expand_list($mlist, 'hlsaudiogood', 'hafmed,hlsmed,hlsaudioworst');
941
- $mlist = main::expand_list($mlist, 'hlsaudioworst', 'haflow,hlalow');
942
- }
943
- # default on-demand radio
944
- if ( $prog->{type} eq "radio" && $mlist =~ /radio/ ) {
945
- $mlist = main::expand_list($mlist, 'radio', 'radiodefault');
946
- $mlist = main::expand_list($mlist, 'radiodefault', 'radiobest');
947
- $mlist = main::expand_list($mlist, 'radiobest', 'hafhigh,hlahigh,dafhigh,radiobetter');
948
- $mlist = main::expand_list($mlist, 'radiobetter', 'hafstd,hlastd,dafstd,radiogood');
949
- $mlist = main::expand_list($mlist, 'radiogood', 'hafmed,hlamed,dafmed,radioworst');
950
- $mlist = main::expand_list($mlist, 'radioworst', 'haflow,hlalow,daflow');
951
- }
952
- # default on-demand tv
953
- if ( $prog->{type} eq "tv" && $mlist =~ /tv/ ) {
954
- $mlist = main::expand_list($mlist, 'tv', 'tvdefault');
955
- $mlist = main::expand_list($mlist, 'tvdefault', 'tvbest');
956
- if ( $opt->{fps25} ) {
957
- $mlist = main::expand_list($mlist, 'tvbest', 'tvbetter');
958
- $mlist = main::expand_list($mlist, 'tvbetter', 'hvfxsd,dvfxsd,tvgood');
959
- $mlist = main::expand_list($mlist, 'tvgood', 'hvfxhigh,dvfxhigh,tvworst');
960
- } else {
961
- $mlist = main::expand_list($mlist, 'tvbest', 'hvfhd,dvfhd,tvbetter');
962
- $mlist = main::expand_list($mlist, 'tvbetter', 'hvfsd,dvfsd,hvfxsd,dvfxsd,tvgood');
963
- $mlist = main::expand_list($mlist, 'tvgood', 'hvfhigh,dvfhigh,hvfxhigh,dvfxhigh,tvworst');
964
- }
965
- $mlist = main::expand_list($mlist, 'tvworst', 'hvflow,dvflow');
966
- }
967
- # single quality level tv
968
- if ( $prog->{type} eq "tv" && $mlist =~ /\btv(hd|sd|high|low)\b/ ) {
969
- if ( $opt->{fps25} ) {
970
- $mlist = main::expand_list($mlist, 'tvsd', 'hvfxsd,dvfxsd');
971
- $mlist = main::expand_list($mlist, 'tvhigh', 'hvfxhigh,dvfxhigh');
972
- } else {
973
- $mlist = main::expand_list($mlist, 'tvhd', 'hvfhd,dvfhd');
974
- $mlist = main::expand_list($mlist, 'tvsd', 'hvfsd,dvfsd,hvfxsd,dvfxsd');
975
- $mlist = main::expand_list($mlist, 'tvhigh', 'hvfhigh,dvfhigh,hvfxhigh,dvfxhigh');
976
- }
977
- $mlist = main::expand_list($mlist, 'tvlow', 'hvflow,dvflow');
978
- }
979
- # single quality level radio
980
- if ( $prog->{type} eq "radio" && $mlist =~ /\bradio(high|std|med|low)\b/ ) {
981
- $mlist = main::expand_list($mlist, 'radiohigh', 'hafhigh,hlahigh,dafhigh');
982
- $mlist = main::expand_list($mlist, 'radiostd', 'hafstd,hlastd,dafstd');
983
- $mlist = main::expand_list($mlist, 'radiomed', 'hafmed,hlamed,dafmed');
984
- $mlist = main::expand_list($mlist, 'radiolow', 'haflow,hlalow,daflow');
985
+ # Defaults
986
+ if ( ! $mlist ) {
987
+ $mlist = 'default';
988
+ }
989
+ if ( $prog->{type} eq "tv" ) {
990
+ if ( $mlist =~ /x(sd|web)/ ) {
991
+ $mlist =~ s/x(sd|web)/$1/g;
992
+ $tvlbr = 1;
993
+ }
994
+ if ( $opt->{audioonly} && $prog->{version} eq "audiodescribed" ) {
995
+ # for 128k audio
996
+ $mlist =~ s/default/hd,web,sd,mobile/g;
997
+ } else {
998
+ $mlist =~ s/default/hd,sd,web,mobile/g;
999
+ }
1000
+ # ensure valid mode for audiodescribed
1001
+ if ( $prog->{version} eq "audiodescribed" && $mlist !~ /(sd|web|mobile)/ ) {
1002
+ if ( $opt->{audioonly} ) {
1003
+ # for 128k audio
1004
+ $mlist .= ",web,sd,mobile";
1005
+ } else {
1006
+ $mlist .= ",sd,web,mobile";
1007
+ }
1008
+ }
1009
+ } elsif ( $prog->{type} eq "radio" ) {
1010
+ $mlist =~ s/default/high,std,med,low/g;
1011
}
1012
+ $mlist =~ s/${regex_alias}/$replace_alias->{$1}/g;
1013
# remove duplicates
1014
my %seen;
1015
- $mlist = join( ",", grep { my $seen = $seen{$_}; $seen{$_} = 1; !$seen } split( ",", $mlist ) );
1016
+ $mlist = join( ",", grep { my $seen = $seen{$_}; $seen{$_} = 1; !$seen } split( /,/, $mlist ) );
1017
+ # tv
1018
+ $mlist = main::expand_list($mlist, 'fhd', "dashfhd,hlsfhd");
1019
+ $mlist = main::expand_list($mlist, 'hd', "hlshd,dashhd");
1020
+ if ( $tvlbr ) {
1021
+ $mlist = main::expand_list($mlist, 'sd', "hlsxsd,dashxsd,hlssd,dashsd");
1022
+ $mlist = main::expand_list($mlist, 'web', "hlsxweb,dashxweb,hlsweb,dashweb");
1023
+ } else {
1024
+ $mlist = main::expand_list($mlist, 'sd', "hlssd,dashsd,hlsxsd,dashxsd");
1025
+ $mlist = main::expand_list($mlist, 'web', "hlsweb,dashweb,hlsxweb,dashxweb");
1026
+ }
1027
+ $mlist = main::expand_list($mlist, 'mobile', "hlsmobile,dashmobile");
1028
+ # radio
1029
+ $mlist = main::expand_list($mlist, 'high', "hlshigh,dashhigh");
1030
+ $mlist = main::expand_list($mlist, 'std', "hlsstd,dashstd");
1031
+ $mlist = main::expand_list($mlist, 'med', "hlsmed,dashmed");
1032
+ $mlist = main::expand_list($mlist, 'low', "hlslow,dashlow");
1033
return $mlist;
1034
}
1035
1036
+sub qualities_from_modes {
1037
+ my $prog = shift;
1038
+ my $modelist = shift;
1039
+ my $regex_quality = qr/(fhd|hd|sd|web|mobile|high|std|med|low)/;
1040
+ my @q1 = map { $_ =~ s/^(hls|dash)x?${regex_quality}.*?$/$2/; $_; } split( /,/, $modelist );
1041
+ my @q2 = grep(/^${regex_quality}$/, @q1);
1042
+ my %seen;
1043
+ my @q3 = grep { my $seen = $seen{$_}; $seen{$_} = 1; !$seen } @q2;
1044
+ return join(",", @q3);
1045
+}
1046
+
1047
+sub qualitysizes_from_modesizes {
1048
+ my $prog = shift;
1049
+ my $modesizes = shift;
1050
+ my $regex_quality = qr/(fhd|hd|sd|web|mobile|high|std|med|low)/;
1051
+ my %sizes;
1052
+ my %units;
1053
+ my @q1 = map {
1054
+ $_ =~ m/^(hls|dash)x?${regex_quality}.*?=(\d+)(.*?)$/;
1055
+ if ( $2 ) {
1056
+ if ( $3 > $sizes{$2} ) {
1057
+ $sizes{$2} = $3;
1058
+ $units{$2} = $4;
1059
+ }
1060
+ };
1061
+ $2;
1062
+ } split( /,/, $modesizes );
1063
+ my @q2 = grep(/^${regex_quality}$/, @q1);
1064
+ my @q3 = map { "$_=$sizes{$_}$units{$_}" } @q2;
1065
+ my %seen;
1066
+ my @q4 = grep { my $seen = $seen{$_}; $seen{$_} = 1; !$seen } @q3;
1067
+ return join(",", @q4);
1068
+}
1069
+
1070
sub postproc {
1071
my ( $prog, $audio_file, $video_file, $ua ) = @_;
1072
my @cmd;
1073
1074
'national' => {
1075
'bbc_one' => 'BBC One',
1076
'bbc_two' => 'BBC Two',
1077
+ 'bbc_three' => 'BBC Three',
1078
'bbc_four' => 'BBC Four',
1079
'bbc_sport' => 'BBC Sport',
1080
'cbbc' => 'CBBC',
1081
1082
'p00fzl6g' => 'BBC News', # bbcnews/programmes/schedules
1083
'p00fzl6n' => 'BBC One', # bbcone/programmes/schedules/hd
1084
'p00fzl73' => 'BBC Parliament', # bbcparliament/programmes/schedules
1085
+ 'p00fzl95' => 'BBC Three', # bbcthree/programmes/schedules
1086
'p015pksy' => 'BBC Two', # bbctwo/programmes/schedules/hd
1087
'p00fzl9r' => 'CBBC', # cbbc/programmes/schedules
1088
'p00fzl9s' => 'CBeebies', # cbeebies/programmes/schedules
1089
1090
# Class cmdline Options
1091
sub opt_format {
1092
return {
1093
- tvmode => [ 1, "tvmode|tv-mode|vmode=s", 'Recording', '--tvmode <mode>,<mode>,...', "TV recording modes (overrides --modes): dvfhd,dvfsd,dvfxsd,dvfhigh,dvfxhigh,dvflow,hvfhd,hvfsd,hvfxsd,hvfhigh,hvfxhigh,hvflow. Shortcuts: best,better,good,worst,dvf,hvf,dash,hls,hd,sd,high,low. 50fps streams (if available) preferred unless --fps25 specified (default=hvfhd,dvfhd,hvfsd,dvfsd,hvfxsd,dvfxsd,hvfhigh,dvfhigh,hvfxhigh,dvfxhigh,hvflow,dvflow)."],
1094
commandtv => [ 1, "commandtv|command-tv=s", 'Output', '--command-tv <command>', "User command to run after successful recording of TV programme. Use substitution parameters in command string (see docs for list). Overrides --command."],
1095
+ fps25 => [ 1, "fps25|tvlbr|tvlowerbitrate|tv-lower-bitrate!", 'Recording', '--tv-lower-bitrate', "Prefer 25fps (or lower-bitrate 50fps) streams for TV programmes if available."],
1096
outputtv => [ 1, "outputtv|output-tv=s", 'Output', '--output-tv <dir>', "Output directory for tv recordings (overrides --output)"],
1097
+ tvmode => [ 0, "tvmode|tv-mode|vmode|tvquality|tv-quality|vquality=s", 'Recording', '--tv-quality <quality>,<quality>,...', "TV recording quality preference (overrides --quality): fhd,hd,sd,web,mobile,default (Aliases: 1080p,720p,540p,396p,288p). Comma-delimited list in descending order of preference. Default: hd,sd,web,mobile"],
1098
};
1099
}
1100
1101
1102
decode_entities($name);
1103
decode_entities($episode);
1104
decode_entities($desc);
1105
+ $name =~ s/\|/-/g;
1106
+ $episode =~ s/\|/-/g;
1107
+ $desc =~ s/\|/-/g;
1108
$prog->{$pid} = main::progclass($prog_type)->new(
1109
'pid' => $pid,
1110
'name' => $name,
1111
1112
decode_entities($name);
1113
decode_entities($episode);
1114
decode_entities($desc);
1115
+ $name =~ s/\|/-/g;
1116
+ $episode =~ s/\|/-/g;
1117
+ $desc =~ s/\|/-/g;
1118
$prog->{$pid} = main::progclass($prog_type)->new(
1119
'pid' => $pid,
1120
'name' => $name,
1121
1122
if ( ! $opt->{raw} ) {
1123
$prog->ffmpeg_init();
1124
# require ffmpeg for HLS
1125
- if ( $mode =~ /^(hls|hvf|haf)/ && ! $opt->{raw} && ! main::exists_in_path('ffmpeg') ) {
1126
+ if ( $mode =~ /^hls/ && ! $opt->{raw} && ! main::exists_in_path('ffmpeg') ) {
1127
main::logger "WARNING: Required ffmpeg utility not found - not converting .ts file(s)\n";
1128
$opt->{raw} = 1;
1129
}
1130
# cannot convert hvf with avconv or ffmpeg < 2.5
1131
- if ( $mode =~ /^hvf/ && ! $opt->{raw} ) {
1132
+ if ( $mode =~ /^hls/ && $prog->{type} eq "tv" && ! $opt->{raw} ) {
1133
if ( $opt->{myffmpegav} ) {
1134
- main::logger "WARNING: avconv does not support conversion of hvf downloads to MP4 - not converting .ts file\n";
1135
+ main::logger "WARNING: avconv does not support conversion of HLS downloads to MP4 - not converting .ts file\n";
1136
$opt->{raw} = 1;
1137
} elsif ( $opt->{myffmpegxx} ) {
1138
- main::logger "WARNING: Unable to determine ffmpeg version - MP4 conversion for hvf downloads may fail\n";
1139
+ main::logger "WARNING: Unable to determine ffmpeg version - MP4 conversion for HLS downloads may fail\n";
1140
} elsif ( ! $opt->{myffmpeg25} ) {
1141
- main::logger "WARNING: Your version of ffmpeg ($opt->{myffmpegversion}) does not support conversion of hvf downloads to MP4 - not converting .ts file\n";
1142
+ main::logger "WARNING: Your version of ffmpeg ($opt->{myffmpegversion}) does not support conversion of HLS downloads to MP4 - not converting .ts file\n";
1143
$opt->{raw} = 1;
1144
}
1145
if ( $opt->{myffmpegav} || $opt->{myffmpegxx} || ! $opt->{myffmpeg25} ) {
1146
- main::logger "WARNING: ffmpeg 2.5 or higher is required to convert hvf downloads to MP4\n";
1147
+ main::logger "WARNING: ffmpeg 2.5 or higher is required to convert HLS downloads to MP4\n";
1148
main::logger "WARNING: Use --raw to bypass MP4 conversion and retain .ts file\n";
1149
main::logger "WARNING: Use --ffmpeg-force to override checks and force MP4 conversion attempt\n";
1150
}
1151
}
1152
# require ffmpeg for DASH
1153
- if ( $mode =~ /^(daf|dvf)/ && ( ! $opt->{raw} || $opt->{mpegts} ) && ! main::exists_in_path('ffmpeg') ) {
1154
+ if ( $mode =~ /^dash/ && ( ! $opt->{raw} || $opt->{mpegts} ) && ! main::exists_in_path('ffmpeg') ) {
1155
main::logger "WARNING: Required ffmpeg utility not found - not converting .m4a and .m4v files\n";
1156
$opt->{raw} = 1;
1157
delete $opt->{mpegts};
1158
}
1159
# cannot convert dvf with avconv or ffmpeg < 3.0
1160
- if ( $mode =~ /^dvf/ && ( ! $opt->{raw} || $opt->{mpegts} ) ) {
1161
+ if ( $mode =~ /^dash/ && $prog->{type} eq "tv" && ( ! $opt->{raw} || $opt->{mpegts} ) ) {
1162
if ( $opt->{myffmpegav} ) {
1163
- main::logger "WARNING: avconv does not support conversion of dvf downloads to MPEG-TS/MP4 - not converting .m4a and .m4v files\n";
1164
+ main::logger "WARNING: avconv does not support conversion of MPEG-DASH downloads to MPEG-TS/MP4 - not converting .m4a and .m4v files\n";
1165
$opt->{raw} = 1;
1166
delete $opt->{mpegts};
1167
} elsif ( $opt->{myffmpegxx} ) {
1168
- main::logger "WARNING: Unable to determine ffmpeg version - MPEG-TS/MP4 conversion for dvf downloads may fail\n";
1169
+ main::logger "WARNING: Unable to determine ffmpeg version - MPEG-TS/MP4 conversion for MPEG-DASH downloads may fail\n";
1170
} elsif ( ! $opt->{myffmpeg30} ) {
1171
- main::logger "WARNING: Your version of ffmpeg ($opt->{myffmpegversion}) does not support conversion of dvf downloads to MPEG-TS/MP4 - not converting .m4a and .m4v files\n";
1172
+ main::logger "WARNING: Your version of ffmpeg ($opt->{myffmpegversion}) does not support conversion of MPEG-DASH downloads to MPEG-TS/MP4 - not converting .m4a and .m4v files\n";
1173
$opt->{raw} = 1;
1174
delete $opt->{mpegts};
1175
}
1176
if ( $opt->{myffmpegav} || $opt->{myffmpegxx} || ! $opt->{myffmpeg30} ) {
1177
- main::logger "WARNING: ffmpeg 3.0 or higher is required to convert dvf downloads to MPEG-TS/MP4\n";
1178
+ main::logger "WARNING: ffmpeg 3.0 or higher is required to convert MPEG-DASH downloads to MPEG-TS/MP4\n";
1179
main::logger "WARNING: Use --raw to bypass MPEG-TS/MP4 conversion and retain .m4a and .m4v files\n";
1180
main::logger "WARNING: Use --ffmpeg-force to override checks and force MPEG-TS/MP4 conversion attempt\n";
1181
}
1182
1183
if ( ! $opt->{nowrite} ) {
1184
# set mode
1185
$prog->{mode} = $mode;
1186
+ $prog->{quality} = $prog->qualities_from_modes($mode);
1187
1188
# Disable proxy here if required
1189
main::proxy_disable($ua) if $opt->{partialproxy};
1190
1191
# Class cmdline Options
1192
sub opt_format {
1193
return {
1194
- radiomode => [ 1, "radiomode|radio-mode|amode=s", 'Recording', '--radiomode <mode>,<mode>,...', "Radio recording modes (overrides --modes): dafhigh,dafstd,dafmed,daflow,hafhigh,hafstd,hafmed,haflow,hlahigh,hlastd,hlsmed,hlalow. Shortcuts: best,better,good,worst,haf,hla,daf,hls,dash,high,std,med,low (default=hafhigh,hlahigh,dafhigh,hafstd,hlastd,dafstd,hafmed,hlamed,dafmed,haflow,hlalow,daflow)."],
1195
commandradio => [ 1, "commandradio|command-radio=s", 'Output', '--command-radio <command>', "User command to run after successful recording of radio programme. Use substitution parameters in command string (see docs for list). Overrides --command."],
1196
outputradio => [ 1, "outputradio|output-radio=s", 'Output', '--output-radio <dir>', "Output directory for radio recordings (overrides --output)"],
1197
+ radiomode => [ 0, "radiomode|radio-mode|amode|radioquality|radio-quality|aquality=s", 'Recording', '--radio-quality <quality>,<quality>,...', "Radio recording quality preference (overrides --quality): high,std,med,low,default (Aliases: 320k,128k,96k,48k). Comma-delimited list in descending order of preference. Default: high,std,med,low."],
1198
};
1199
}
1200
1201
1202
$opt->{info} = 0;
1203
# Do the recording (force --get option)
1204
$opt->{get} = 1;
1205
+ # reset for PVR search
1206
+ delete $opt->{nowarnmoderemap};
1207
1208
my $failcount = 0;
1209
if ( $pvr->{$name}->{pid} ) {
1210
1211
}
1212
$retcode += $failcount;
1213
}
1214
- main::purge_warning( $hist, 30 );
1215
return $retcode;
1216
}
1217
1218
1219
return 1;
1220
}
1221
# Parse valid options and create array (ignore options from the options files that have not been overriden on the cmdline)
1222
- for ( grep !/(^cache|profiledir|encoding.*|silent|webrequest|future|nocopyright|^test|metadataonly|subsonly|thumbonly|cuesheetonly|tracklistonly|creditsonly|tagonly|^get|refresh|^save|^prefs|help|expiry|tree|terse|streaminfo|listformat|^list|showoptions|hide|info|pvr.*|^purge|markdownloaded)$/, sort {lc $a cmp lc $b} keys %{$opt_cmdline} ) {
1223
+ for ( grep !/(^cache|profiledir|encoding.*|silent|webrequest|future|nocopyright|^test|metadataonly|subsonly|thumbonly|cuesheetonly|tracklistonly|creditsonly|tagonly|^get|refresh|^save|^prefs|help|expiry|tree|terse|streaminfo|listformat|^list|showoptions|hide|info|pvr.*|markdownloaded)$/, sort {lc $a cmp lc $b} keys %{$opt_cmdline} ) {
1224
if ( defined $opt_cmdline->{$_} ) {
1225
push @options, "$_ $opt_cmdline->{$_}";
1226
main::logger "DEBUG: Adding option $_ = $opt_cmdline->{$_}\n" if $opt->{debug};
1227
get_iplayer-3.28.tar.gz/get_iplayer.1 -> get_iplayer-3.29.tar.gz/get_iplayer.1
Changed
118
1
2
-.TH GET_IPLAYER "1" "December 2021" "Phil Lewis" "get_iplayer Manual"
3
+.TH GET_IPLAYER "1" "February 2022" "Phil Lewis" "get_iplayer Manual"
4
.SH NAME
5
get_iplayer \- Stream Recording tool and PVR for BBC iPlayer and BBC Sounds
6
.SH SYNOPSIS
7
8
Number of attempts to make or resume a failed connection. \-\-attempts is applied per\-stream, per\-mode. Many modes have two or more streams available.
9
.TP
10
\fB\-\-audio\-only
11
-Only download audio stream for TV programme. 'hls' recording modes are not supported and ignored. Produces .m4a file. Implies \-\-force.
12
+Only download audio stream for TV programme. Produces .m4a file. Implies \-\-force.
13
.TP
14
-\fB\-\-download\-abortonfail
15
-Exit immediately if stream for any recording mode fails to download. Use to avoid repeated failed download attempts if connection is dropped or access is blocked.
16
+\fB\-\-download\-abort\-onfail
17
+Exit immediately if any stream to download. Use to avoid repeated failed download attempts if connection is dropped or access is blocked.
18
+.TP
19
+\fB\-\-exclude\-format <format>,<format>,...
20
+Comma\-separated list of media stream formats to ignore when recording. Valid values: hls,dash.
21
.TP
22
\fB\-\-exclude\-supplier <supplier>,<supplier>,...
23
Comma\-separated list of media stream suppliers (CDNs) to skip. Possible values: akamai,limelight,bidi,cloudfront. Synonym: \-\-exclude\-cdn.
24
25
\fB\-\-force
26
Ignore programme history (unsets \-\-hide option also).
27
.TP
28
-\fB\-\-fps25
29
-Use only 25fps streams for TV programmes (HD video not available).
30
-.TP
31
\fB\-\-get, \-g
32
Start recording matching programmes. Search terms required.
33
.TP
34
\fB\-\-hash
35
Show recording progress as hashes
36
.TP
37
+\fB\-\-include\-format <format>,<format>,...
38
+Comma\-separated list of media stream to use when recording. Overrides \-\-exclude\-format. Valid values: hls,dash
39
+.TP
40
\fB\-\-include\-supplier <supplier>,<supplier>,...
41
Comma\-separated list of media stream suppliers (CDNs) to use if not included by default or if previously excluded by \-\-exclude\-supplier. Possible values: akamai,limelight,bidi,cloudfront. Synonym: \-\-include\-cdn.
42
.TP
43
44
\fB\-\-mark\-downloaded
45
Mark programmes in search results or specified with \-\-pid/\-\-url as downloaded by inserting records in download history.
46
.TP
47
-\fB\-\-modes <mode>,<mode>,...
48
-Recording modes. See \-\-tvmode and \-\-radiomode (with \-\-long\-help) for available modes and defaults. Shortcuts: tvbest,tvbetter,tvgood,tvworst,radiobest,radiobetter,radiogood,radioworst (default=default for programme type).
49
-.TP
50
\fB\-\-no\-merge\-versions
51
Do not merge programme versions with same name and duration.
52
.TP
53
54
\fB\-\-proxy, \-p <url>
55
Web proxy URL, e.g., http://username:password@server:port or http://server:port. Value of http_proxy environment variable (if present) will be used unless \-\-proxy is specified. Used for both HTTP and HTTPS. Overridden by \-\-no\-proxy.
56
.TP
57
-\fB\-\-radiomode <mode>,<mode>,...
58
-Radio recording modes (overrides \-\-modes): dafhigh,dafstd,dafmed,daflow,hafhigh,hafstd,hafmed,haflow,hlahigh,hlastd,hlsmed,hlalow. Shortcuts: best,better,good,worst,haf,hla,daf,hls,dash,high,std,med,low (default=hafhigh,hlahigh,dafhigh,hafstd,hlastd,dafstd,hafmed,hlamed,dafmed,haflow,hlalow,daflow).
59
+\fB\-\-quality <quality>,<quality>,...
60
+TV and radio recording quality preference. See \-\-tv\-quality and \-\-radio\-quality for available values and defaults. Default: default for programme type.
61
+.TP
62
+\fB\-\-radio\-quality <quality>,<quality>,...
63
+Radio recording quality preference (overrides \-\-quality): high,std,med,low,default (Aliases: 320k,128k,96k,48k). Comma\-delimited list in descending order of preference. Default: high,std,med,low.
64
.TP
65
\fB\-\-start <secs|hh:mm:ss>
66
Recording/streaming start offset (actual start may be several seconds earlier for HLS and DASH streams)
67
68
\fB\-\-test, \-t
69
Test only \- no recording (only shows search results with \-\-pvr and \-\-pid\-recursive)
70
.TP
71
-\fB\-\-tvmode <mode>,<mode>,...
72
-TV recording modes (overrides \-\-modes): dvfhd,dvfsd,dvfxsd,dvfhigh,dvfxhigh,dvflow,hvfhd,hvfsd,hvfxsd,hvfhigh,hvfxhigh,hvflow. Shortcuts: best,better,good,worst,dvf,hvf,dash,hls,hd,sd,high,low. 50fps streams (if available) preferred unless \-\-fps25 specified (default=hvfhd,dvfhd,hvfsd,dvfsd,hvfxsd,dvfxsd,hvfhigh,dvfhigh,hvfxhigh,dvfxhigh,hvflow,dvflow).
73
+\fB\-\-tv\-lower\-bitrate
74
+Prefer 25fps (or lower\-bitrate 50fps) streams for TV programmes if available.
75
+.TP
76
+\fB\-\-tv\-quality <quality>,<quality>,...
77
+TV recording quality preference (overrides \-\-quality): fhd,hd,sd,web,mobile,default (Aliases: 1080p,720p,540p,396p,288p). Comma\-delimited list in descending order of preference. Default: hd,sd,web,mobile
78
.TP
79
\fB\-\-url <url>,<url>,...
80
Record the PIDs contained in the specified iPlayer episode URLs. Alias for \-\-pid.
81
82
\fB\-\-limit\-matches <number>
83
Limits the number of matching results for any search (and for every PVR search)
84
.TP
85
-\fB\-\-nopurge
86
-Don't show warning about programmes recorded over 30 days ago
87
-.TP
88
\fB\-\-prefs\-add
89
Add/Change specified saved user or preset options
90
.TP
91
92
\fB\-\-index\-maxconn <number>
93
Maximum number of connections to use for concurrent programme indexing. Default: 5 Min: 1 Max: 10
94
.TP
95
-\fB\-\-purge\-files
96
-Delete downloaded programmes more than 30 days old
97
-.TP
98
\fB\-\-release\-check
99
Forces check for new release if used on command line. Checks for new release weekly if saved in preferences.
100
.TP
101
\fB\-\-throttle <Mb/s>
102
Bandwidth limit (in Mb/s) for media file download. Default: unlimited. Synonym: \-\-bw
103
-.TP
104
-\fB\-\-trim\-history <# days to retain>
105
-Remove download history entries older than number of days specified in option value. Cannot specify 0 \- use 'all' to completely delete download history
106
.SS "Deprecated Options:"
107
.TP
108
\fB\-\-no\-index\-concurrent
109
110
.PP
111
This manual page was originally written by Jonathan Wiltshire <jmw@debian.org> for the Debian project (but may be used by others).
112
.SH COPYRIGHT NOTICE
113
-get_iplayer v3.28, Copyright (C) 2008\-2010 Phil Lewis
114
+get_iplayer v3.29, Copyright (C) 2008\-2010 Phil Lewis
115
This program comes with ABSOLUTELY NO WARRANTY; for details use \-\-warranty.
116
This is free software, and you are welcome to redistribute it under certain
117
conditions; use \-\-conditions for details.
118
get_iplayer-3.28.tar.gz/get_iplayer.cgi -> get_iplayer-3.29.tar.gz/get_iplayer.cgi
Changed
32
1
2
# License: GPLv3 (see LICENSE.txt)
3
#
4
5
-my $VERSION = 3.28;
6
+my $VERSION = 3.29;
7
my $VERSION_TEXT;
8
$VERSION_TEXT = sprintf("v%.2f", $VERSION) unless $VERSION_TEXT;
9
10
11
};
12
13
$opt->{MODES} = {
14
- title => 'Recording Modes', # Title
15
- tooltip => 'Comma separated list of recording modes which should be tried in order. Default is "best" for HD TV (if available, with fallback to SD TV). Set to "better" (without quotes) for best available SD TV. Set to "good" (without quotes) for lower-quality SD TV.', # Tooltip
16
+ title => 'Recording Quality', # Title
17
+ tooltip => 'Comma separated list of recording quality settings which should be tried in order', # Tooltip
18
webvar => 'MODES', # webvar
19
optkey => 'modes', # option
20
type => 'text', # type
21
22
};
23
24
$opt->{FPS25} = {
25
- title => 'Use only 25fps streams',
26
- tooltip => "Use only 25fps media streams. HD video not available.",
27
+ title => 'Prefer lower-bitrate TV streams',
28
+ tooltip => "Prefer lower-bitrate TV streams",
29
webvar => 'FPS25',
30
optkey => 'fps25',
31
type => 'radioboolean',
32
Refresh
No build results available
Refresh
No rpmlint results available
Login required, please
login
or
signup
in order to comment
Request History
develop7 created request about 3 years ago
get_iplayer 3.29
develop7 accepted request almost 3 years ago
self-accept