Projects
Extra
get_iplayer
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 11
View file
get_iplayer.changes
Changed
@@ -1,4 +1,12 @@ ------------------------------------------------------------------- +Tue Feb 8 10:40:09 UTC 2022 - Andrei Dziahel <develop7@develop7.info> + +- get_iplayer 3.29 + + See https://github.com/get-iplayer/get_iplayer/wiki/release320to329#release329 + for upstream release notes + +------------------------------------------------------------------- Mon Feb 7 18:56:44 UTC 2022 - Andrei Dziahel <develop7@develop7.info> - get_iplayer v3.28
View file
get_iplayer.spec
Changed
@@ -6,7 +6,7 @@ # Name: get_iplayer -Version: 3.28 +Version: 3.29 Release: 0 Summary: Downloads H.264 BBC IPlayer TV, Radio, and Podcast Programs License: GPL-3.0-only
View file
get_iplayer-3.28.tar.gz/.github/ISSUE_TEMPLATE/bug_report.md
Deleted
@@ -1,35 +0,0 @@ ---- -name: Bug report -about: Create a bug report -title: '' -labels: '' -assignees: '' - ---- -### *Remove this line and all the text below before submitting your bug report* - -#### Read first - -- Do not request help with using get_iplayer. No user support will be provided. -- Do not request new features. Feature requests will not be accepted. -- All bug reports will automatically be closed and locked upon receipt. -- If your report identifies a reproducible bug in get_iplayer, it will be re-opened until a fix is released. -- You will receive no communication from the developers, so provide all the information required. - -#### What you need to provide - -- A clear and concise description of the bug. -- The **complete get_iplayer command line** used. -- The PID or URL of the programme you attempted to download, if applicable. **Only provide one programme**. -- 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: - - get_iplayer [your options here] --verbose > log.txt 2>&1 - -- Screenshots, if the bug appears to be in the Web PVR Manager user interface. -- OS and version [e.g. Windows 10 2004, macOS 10.15.5, Ubuntu 20.04.1] -- Browser and version [e.g. Chrome 83.0.4103, Edge 83.0.478.56, Firefox 78.0.1, Safari 13.1.1] -- get_iplayer version [e.g. 3.26, 3.26.0-MSWin32, 3.26.1-darwin] - use `get_iplayer -V` - -#### Documentation changes - -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.
View file
get_iplayer-3.29.tar.gz/.github/ISSUE_TEMPLATE/bug_report.yaml
Added
@@ -0,0 +1,86 @@ +name: Bug Report +description: File a bug report +body: +- type: markdown + attributes: + value: | + ## Read First + * Do not request help with using get_iplayer. User support will not be provided. + * Do not request new features for get_iplayer. Feature requests will not be accepted. + * All bug reports will automatically be closed and locked upon receipt. + * If your report identifies a reproducible bug in get_iplayer, it will be re-opened until a fix is released. + * You will receive no communication from the developers, so provide all the information required. +- type: checkboxes + attributes: + label: Do not file a bug report if you are using get_iplayer outside the UK. If you do, your report will be ignored. + options: + - label: I am not using get_iplayer outside the UK + required: true +- type: checkboxes + attributes: + 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. + options: + - label: I am not using get_iplayer with a VPN or proxy from any location, including the UK + required: true +- type: checkboxes + attributes: + 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. + description: See - **[Issue Tracker](https://github.com/get-iplayer/get_iplayer/issues)** + options: + - label: I have searched the repository and found no existing reports of the bug in the issue tracker + required: true +- type: checkboxes + attributes: + 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. + 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) + options: + - label: I have reviewed recent open and closed entries in the issue tracker and found no existing reports of the bug + required: true +- type: checkboxes + attributes: + label: Ensure that you are using get_iplayer 3.29 or higher. If not, your report will be ignored. + description: Check version with **`get_iplayer -V` or see the bottom of the Web PVR search page** + options: + - label: I am using get_iplayer 3.29 or higher + required: true +- type: input + attributes: + label: Identify the operating system and version where get_iplayer demonstrates the bug + description: Examples - Ubuntu 20.04, Fedora 34, Arch, OpenBSD 6.8, macOS 10.14, macOS Monterey (M1), Windows 10 + validations: + required: true +- type: textarea + attributes: + 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. + validations: + required: true +- type: input + attributes: + 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. + validations: + required: false +- type: textarea + attributes: + 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. + validations: + required: true +- type: markdown + attributes: + value: | + ## Create a verbose log file for the bug, e.g., "log.txt" using the `--verbose` option: + ```bash + get_iplayer [your options here] --verbose > log.txt 2>&1 + ``` + ### 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. + ### Do not submit screenshots instead of a log file. If you do, your report will be ignored. +- type: textarea + attributes: + 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. + validations: + required: true +- type: input + attributes: + 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) + description: Examples - Chrome 83.0.4103, Edge 83.0.478.56, Firefox 78.0.1, Safari 13.1.1 + validations: + required: false
View file
get_iplayer-3.29.tar.gz/.github/PULL_REQUEST_TEMPLATE.md
Added
@@ -0,0 +1,11 @@ +### Read first + +- Fixes for reproducible bugs are welcome. +- Do not submit new features. New features will not be merged. +- Do not submit enhancements. Enhancements will not be merged. +- Do not submit changes to existing behaviour. Changes to existing behaviour will not be merged. +- All pull requests will automatically be closed and locked upon receipt. +- If your pull request fixes a reproducible bug, it will be re-opened until it is merged. +- You will receive no communication from the developers, so ensure your pull request is complete. + +#### Delete this line and all text above before submitting your pull request.
View file
get_iplayer-3.29.tar.gz/.github/workflows/auto-close-lock-pr-action.yml
Added
@@ -0,0 +1,29 @@ +on: + pull_request: + types: + - opened +jobs: + auto-close-lock-pr-action_job: + runs-on: ubuntu-latest + name: auto-close-lock-pr-action_job + steps: + - name: auto-close-lock-pr-action_close-step + id: auto-close-lock-pr-action_close-step + uses: maxkomarychev/octions/octions/issues/update@master + with: + token: ${{ secrets.GITHUB_TOKEN }} + issue_number: ${{ github.event.number }} + state: closed + - name: auto-close-lock-pr-action_lock-step + id: auto-close-lock-pr-action_lock-step + uses: maxkomarychev/octions/octions/issues/lock@master + with: + token: ${{ secrets.GITHUB_TOKEN }} + issue_number: ${{ github.event.number }} + - name: auto-close-lock-pr-action_label-step + id: auto-close-lock-pr-action_label-step + uses: maxkomarychev/octions/octions/issues/add-labels@master + with: + token: ${{ secrets.GITHUB_TOKEN }} + issue_number: ${{ github.event.number }} + labels: invalid
View file
get_iplayer-3.28.tar.gz/Makefile -> get_iplayer-3.29.tar.gz/Makefile
Changed
@@ -9,13 +9,14 @@ @git checkout master @sed -i.bak -e 's/^\(my $$version = \).*/\1$(VERSION);/' get_iplayer @sed -i.bak -e 's/^\(my $$VERSION = \).*/\1$(VERSION);/' get_iplayer.cgi - @rm -f get_iplayer.bak get_iplayer.cgi.bak + @sed -i.bak -e 's/\(get_iplayer \)[0-9.]\{1,\}\( or higher\)/\1$(VERSION)\2/g' .github/ISSUE_TEMPLATE/bug_report.yaml + @rm -f get_iplayer.bak get_iplayer.cgi.bak .github/ISSUE_TEMPLATE/bug_report.yaml.bak @./get_iplayer --nocopyright --manpage get_iplayer.1 @git diff --exit-code get_iplayer.1 > /dev/null || \ sed -i.bak -e 's/\(\.TH GET_IPLAYER "1" "\)[^"]*"/\1$(shell date +"%B %Y")\"/' get_iplayer get_iplayer.1 @rm -f get_iplayer.bak get_iplayer.1.bak @git log --format='%aN' | sort -u > CONTRIBUTORS; git add CONTRIBUTORS - @git commit -m "Release $(VERSION)" get_iplayer get_iplayer.cgi get_iplayer.1 CONTRIBUTORS + @git commit -m "Release $(VERSION)" get_iplayer get_iplayer.cgi get_iplayer.1 CONTRIBUTORS .github/ISSUE_TEMPLATE/bug_report.yaml @git tag v$(VERSION) tarball:
View file
get_iplayer-3.28.tar.gz/README.md -> get_iplayer-3.29.tar.gz/README.md
Changed
@@ -4,7 +4,7 @@ * Downloads TV and radio programmes from BBC iPlayer/BBC Sounds * Allows multiple programmes to be downloaded using a single command -* Indexing of most available iPlayer/Sounds catch-up programmes from previous 30 days (not BBC Three, Red Button, iPlayer Exclusive, or Podcast-only) +* Indexing of most available iPlayer/Sounds catch-up programmes from previous 30 days (not Red Button, iPlayer Exclusive, or Podcast-only) * Caching of programme index with automatic updating * Regex search on programme name * Regex search on programme description and episode title @@ -81,17 +81,17 @@ *(The `$` regular expression metacharacter matches "Radio 4" only at the end of the channel name, thus avoiding matches against "Radio 4 Extra")* -* Record TV programme number 208 (index from search results) in HD, with SD fallback if HD not available: +* Record TV programme number 208 (index from search results) in HD, with fallback to lower quality if not available: - `get_iplayer --get 208` [default is to download best available (max 1280x720)] + `get_iplayer --get 208` [default setting] - OR - - `get_iplayer --get 208 --tvmode=best` + or + + `get_iplayer --get 208 --tv-quality=hd,sd,web,mobile` [explicit setting] -* Record TV programme number 208 with lower resolution (max 704x396): +* Record TV programme number 208 in lower resolution only (704x396@50): - `get_iplayer --get 208 --tvmode=good` + `get_iplayer --get 208 --tv-quality=web` * Record TV programme number 208 and download subtitles in SubRip (SRT) format: @@ -112,17 +112,18 @@ * Record a radio programme using its Sounds URL: `get_iplayer https://www.bbc.co.uk/sounds/play/b07gcv34` -* Record a radio programme using the PID (b07gcv34) from its Sounds URL in highest quality (320k), with fallback to lower quality if not available: - `get_iplayer --pid=b07gcv34` [default is to download best available] +* 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): + + `get_iplayer --pid=b07gcv34` [default setting] OR - `get_iplayer --pid=b07gcv34 --radiomode=best` + `get_iplayer --pid=b07gcv34 --radio-quality=high,std,med,low` [explicit setting] -* Record a radio programme using the PID (b07gcv34) from its Sounds URL with lower bit rate (96k): +* Record a radio programme using the PID (b07gcv34) from its Sounds URL with lower bit rate only (96k): - `get_iplayer --pid=b07gcv34 --radiomode=good` + `get_iplayer --pid=b07gcv34 --radio-quality=med` * Record multiple radio programmes (using PIDs from Sounds URLs):
View file
get_iplayer-3.28.tar.gz/get_iplayer -> get_iplayer-3.29.tar.gz/get_iplayer
Changed
@@ -24,7 +24,7 @@ # # package main; -my $version = 3.28; +my $version = 3.29; my $version_text; $version_text = sprintf("v%.2f", $version) unless $version_text; # @@ -83,17 +83,18 @@ my $opt_format = { # Recording 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."], - 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."], - 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."], + audioonly => [ 1, "audioonly|audio-only!", 'Recording', '--audio-only', "Only download audio stream for TV programme. Produces .m4a file. Implies --force."], + 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."], + 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."], 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."], force => [ 1, "force|force-download!", 'Recording', '--force', "Ignore programme history (unsets --hide option also)."], - fps25 => [ 1, "fps25!", 'Recording', '--fps25', "Use only 25fps streams for TV programmes (HD video not available)."], get => [ 2, "get|record|g!", 'Recording', '--get, -g', "Start recording matching programmes. Search terms required."], + 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"], 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."], hash => [ 1, "hash!", 'Recording', '--hash', "Show recording progress as hashes"], 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."], 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."], - 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)."], + 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."], nomergeversions => [ 1, "nomergeversions|no-merge-versions!", 'Recording', '--no-merge-versions', "Do not merge programme versions with same name and duration."], noproxy => [ 1, "noproxy|no-proxy!", 'Recording', '--no-proxy', "Ignore --proxy setting in preferences and/or http_proxy environment variable."], overwrite => [ 1, "overwrite|over-write!", 'Recording', '--overwrite', "Overwrite recordings if they already exist"], @@ -170,7 +171,7 @@ 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."], expiry => [ 1, "expiry|e=n", 'Config', '--expiry, -e <secs>', "Cache expiry in seconds (default 4hrs)"], limitmatches => [ 1, "limitmatches|limit-matches=n", 'Config', '--limit-matches <number>', "Limits the number of matching results for any search (and for every PVR search)"], - nopurge => [ 1, "no-purge|nopurge!", 'Config', '--nopurge', "Don't show warning about programmes recorded over 30 days ago"], + nopurge => [ 1, "no-purge|nopurge!", 'Hidden', '--nopurge', "Don't show warning about programmes recorded over 30 days ago"], prefsadd => [ 0, "addprefs|add-prefs|prefsadd|prefs-add!", 'Config', '--prefs-add', "Add/Change specified saved user or preset options"], prefsdel => [ 0, "del-prefs|delprefs|prefsdel|prefs-del!", 'Config', '--prefs-del', "Remove specified saved user or preset options"], prefsclear => [ 0, "clear-prefs|clearprefs|prefsclear|prefs-clear!", 'Config', '--prefs-clear', "Remove *ALL* saved user or preset options"], @@ -234,10 +235,9 @@ 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"], 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"], 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."], - purgefiles => [ 1, "purgefiles|purge-files!", 'Misc', '--purge-files', "Delete downloaded programmes more than 30 days old"], 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."], throttle => [ 1, "bw|throttle=f", 'Misc', '--throttle <Mb/s>', "Bandwidth limit (in Mb/s) for media file download. Default: unlimited. Synonym: --bw"], - 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"], + 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"], }; @@ -388,6 +388,47 @@ # Parse the cmdline using the opt_format hash Options->usage( 0 ) if not $opt_cmdline->parse(); +# check obsolete options on command ilne +my %nono_remap = ( + "mode" => "quality", + "modes" => "quality", + "tvmode" => "tv-quality", + "radiomode" => "radio-quality", + "fps25" => "tv-lower-bitrate", +); +my $nono_regex = "(" . join("|", keys %nono_remap) . ")"; +my @nono_args = map { $_ =~ s/\-//g; $_ =~ s/=.*$//; $_;} grep(/--${nono_regex}/, @argv_save); +my $nono_found; +for my $nono_arg ( @nono_args ) { + $nono_found = 1; + logger "WARNING: --$nono_arg is deprecated and will be removed in a future release. Use --$nono_remap{$nono_arg}.\n"; + # logger "ERROR: --$nono_arg is no longer supported. Use --$nono_remap{$nono_arg}.\n"; +} +# exit 1 if $nono_found; + +# check invalid quality settings on command ilne +my $regex_quality = { + tvmode => qr/(fhd|hd|sd|web|mobile|1080p?|720p?|540p?|396p?|288p?|default)/, + radiomode => qr/(high|std|med|low|320k?|128k?|96k?|48k?|default)/ +}; +$regex_quality->{modes} = "($regex_quality->{tvmode}|$regex_quality->{radiomode})"; +my $bad_quality; +for my $qp ( "", "tv", "radio" ) { + my $qs = $qp ? "" : "s"; + my $cmd_quality = "${qp}quality"; + my $opt_quality = "${qp}mode${qs}"; + my @bad_quality; + if ( $opt_cmdline->{$opt_quality} ) { + @bad_quality = grep(!/^$regex_quality->{$opt_quality}$/, split(/,/, $opt_cmdline->{$opt_quality})); + if ( @bad_quality ) { + $bad_quality = 1; + logger "WARNING: Invalid values in --${cmd_quality}/--${opt_quality}: '" . join(",", @bad_quality) . "'. This will be a fatal error in a future release.\n"; + # logger "ERROR: Invalid values in --${cmd_quality}/--${opt_quality}: '" . join(",", @bad_quality) . "'\n"; + } + } +} +# exit 1 if $bad_quality; + # process --start and --stop if necessary foreach ('start', 'stop') { if ($opt_cmdline->{$_} && $opt_cmdline->{$_} =~ /(\d\d):(\d\d)(:(\d\d))?/) { @@ -416,9 +457,7 @@ # Sanitize preset file name my $presetname = StringUtils::sanitize_path( $opt_cmdline->{preset}, 0, 1 ); $optfile_preset = File::Spec->catfile($presets_dir, $presetname); - logger "INFO: Using user options preset '${presetname}'\n"; } -logger "DEBUG: User preset options file: $optfile_preset\n" if defined $optfile_preset && $opt->{debug}; # Parse options if we're not saving/adding/deleting options (system-wide options are overridden by personal options) if ( ! ( $opt_pre->{prefsadd} || $opt_pre->{prefsdel} || $opt_pre->{prefsclear} ) ) { @@ -561,7 +600,6 @@ } elsif ( $search_args[0] =~ m{^bbc-ipd:/*download/(\w+)/\w+/(\w+)/} ) { $opt->{pid} = $1; - $opt->{modes} ||= "best" if $2 eq "hd"; } } @@ -592,16 +630,8 @@ release_check(); my $retcode = 0; -# Trim history -if ( defined($opt->{trimhistory}) ) { - my $hist = History->new(); - $hist->trim(); -# purge files -} elsif ( $opt->{purgefiles} ) { - my $hist = History->new(); - purge_downloaded_files( $hist, 30 ); # mark downloaded -} elsif ( $opt->{markdownloaded} ) { +if ( $opt->{markdownloaded} ) { if ( ! $opt->{pid} && $no_search_args ) { main::logger "ERROR: Search term(s) or --pid or --url required with --mark-downloaded\n"; exit 1; @@ -702,7 +732,6 @@ my $hist = History->new(); my @pids = split( /,/, $opt->{pid} ); $retcode = download_pid_matches( $hist, find_pid_matches( $hist, @pids ) ); - purge_warning( $hist, 30 ); # Show history } elsif ( $opt->{history} ) { @@ -717,7 +746,6 @@ } my $hist = History->new(); $retcode = download_matches( $hist, find_matches( $hist, @search_args ) ); - purge_warning( $hist, 30 ); } exit $retcode; @@ -1949,67 +1977,6 @@ } # Generic -# Checks history for files that are over 30 days old and asks user if they should be deleted -# "$prog->{pid}|$prog->{name}|$prog->{episode}|$prog->{type}|".time()."|$prog->{mode}|$prog->{filename}\n"; -sub purge_downloaded_files { - my $hist = shift; - my @delete; - my @proglist; - my $days = shift; - - for my $pid ( $hist->get_pids() ) { - my $record = $hist->get_record( $pid ); - if ( $record->{timeadded} < (time() - $days*86400) && $record->{filename} && -f $record->{filename} ) { - # Calculate the seconds difference between epoch_now and epoch_datestring and convert back into array_time - my @t = gmtime( time() - $record->{timeadded} ); - push @proglist, "$record->{name} - $record->{episode}, Recorded: $t[7] days $t[2] hours ago"; - push @delete, $record->{filename}; - } - } - - if ( @delete ) { - main::logger "\nThese programmes are over 30 days old and should be deleted:\n"; - main::logger "-----------------------------------\n"; - main::logger join "\n", @proglist; - main::logger "\n-----------------------------------\n"; - main::logger "Do you wish to delete them now (Yes/No) ?\n"; - my $answer = <STDIN>; - if ($answer =~ /^yes$/i ) { - for ( @delete ) { - main::logger "INFO: Deleting $_\n"; - unlink $_; - } - main::logger "Programmes deleted\n"; - } else { - main::logger "No Programmes deleted\n"; - } - } - - return 0; -} - -sub purge_warning { - my $hist = shift; - my $days = shift; - my $overdue; - return 0 if $opt->{nopurge} || $opt->{nowrite}; - for my $pid ( $hist->get_pids() ) { - my $record = $hist->get_record( $pid ); - if ( $record->{timeadded} < (time() - $days*86400) && $record->{filename} && -f $record->{filename} ) { - $overdue = 1; - last; - } - } - if ( $overdue ) { - print STDOUT "WARNING: You have programmes over 30 days old that should be deleted.\n"; - print STDOUT "WARNING: Find them with 'get_iplayer --history --before=720 \".*\"'\n"; - print STDOUT "WARNING: or use the 'Recordings' tab in the Web PVR Manager.\n"; - print STDOUT "WARNING: Use 'get_iplayer --purge-files' to delete all programmes over 30 days old.\n"; - print STDOUT "WARNING: Use 'get_iplayer --prefs-add --no-purge' to suppress this warning.\n"; - } - return 0; -} - # Returns url decoded string sub url_decode { my $str = shift; @@ -2567,7 +2534,7 @@ 'This applies even if the base option name already begins with "no-", e.g., --no-no-tag or --no-no-artwork', ); push @man, - '.TH GET_IPLAYER "1" "December 2021" "Phil Lewis" "get_iplayer Manual"', + '.TH GET_IPLAYER "1" "February 2022" "Phil Lewis" "get_iplayer Manual"', '.SH NAME', 'get_iplayer - Stream Recording tool and PVR for BBC iPlayer and BBC Sounds', '.SH SYNOPSIS', '\fBget_iplayer\fR [<options>] [<regex|index> ...]', @@ -2715,7 +2682,7 @@ # specify regex of options that cannot be saved sub excludeopts { - return '^(cache|profiledir|encoding|silent|help|debug|get|pvr|prefs|preset|warranty|conditions|dumpoptions|comment|purge|markdownloaded)'; + return '^(cache|profiledir|encoding|silent|help|debug|get|pvr|prefs|preset|warranty|conditions|dumpoptions|comment|markdownloaded)'; } # List all available presets in the specified dir @@ -3042,55 +3009,6 @@ $History::optref = shift; } -sub trim { - my $oldhistoryfile = "$historyfile.old"; - my $newhistoryfile = "$historyfile.new"; - if ( $opt->{trimhistory} =~ /^all$/i ) { - if ( ! copy($historyfile, $oldhistoryfile) ) { - die "ERROR: Cannot copy $historyfile to $oldhistoryfile: $!\n"; - } - if ( ! unlink($historyfile) ) { - die "ERROR: Cannot delete $historyfile: $! \n"; - } - main::logger "INFO: Deleted all entries from download history\n"; - return; - } - if ( $opt->{trimhistory} !~ /^\d+$/ ) { - die "ERROR: --trim-history option must have a positive integer value, or use 'all' to completely delete download history.\n"; - } - if ( $opt->{trimhistory} =~ /^0+$/ ) { - die "ERROR: Cannot specify 0 for --trim-history option. Use 'all' to completely delete download history.\n"; - } - if ( ! open(HIST, "< $historyfile") ) { - die "ERROR: Cannot read from $historyfile\n"; - } - if ( ! open(NEWHIST, "> $newhistoryfile") ) { - die "ERROR: Cannot write to $newhistoryfile\n"; - } - my $trim_limit = time() - ($opt->{trimhistory} * 86400); - my $deleted_count = 0; - while (<HIST>) { - chomp(); - next if /^[\#\s]/; - my @record = split /\|/; - my $timeadded = $record[4]; - if ( $timeadded >= $trim_limit ) { - print NEWHIST "$_\n"; - } else { - $deleted_count++; - } - } - close HIST; - close NEWHIST; - if ( ! copy($historyfile, $oldhistoryfile) ) { - die "ERROR: Cannot copy $historyfile to $oldhistoryfile: $!\n"; - } - if ( ! move($newhistoryfile, $historyfile) ) { - die "ERROR: Cannot move $newhistoryfile to $historyfile: $!\n"; - } - main::logger "INFO: Deleted $deleted_count entries from download history\n"; -} - # Uses global @history_format # Adds prog to history file (with a timestamp) so that it is not rerecorded after deletion sub add { @@ -3571,15 +3489,16 @@ # skip these # If hash then list keys } elsif ( ref$data{$_} eq 'HASH' ) { + next if ( $_ eq "modes" || $_ eq "modesizes") && ! $opt->{verbose}; for my $key ( sort keys %{$data{$_}} ) { main::logger sprintf "%-16s ", $_.':'; if ( ref$data{$_}->{$key} ne 'HASH' ) { - if ( $_ eq "modes" || $_ eq "modesizes" ) { + if ( $_ eq "modes" || $_ eq "modesizes" || $_ eq "qualities" || $_ eq "qualitysizes" ) { my %seen = (); - my @vals = sort Programme::cmp_modes grep { not $seen{$_}++ } map { $_ =~ s/([a-z])\d+/$1/; $_; } split(",", $data{$_}->{$key}); + my @vals = sort Programme::cmp_modes grep { not $seen{$_}++ } map { $_ =~ s/([a-z])\d+/$1/; $_; } split(/,/, $data{$_}->{$key}); my $val = join(",", @vals); main::logger "$key: $val"; - main::logger " [estimated sizes]" if $_ eq "modesizes"; + main::logger " [estimated sizes]" if $_ eq "modesizes" || $_ eq "qualitysizes"; } else { main::logger "$key: $data{$_}->{$key}"; } @@ -3717,25 +3636,23 @@ sub cmp_modes($$) { my ($x, $y) = @_; my %ranks = ( - 'hd(\d+)?' => 1000, - '[^x]sd(\d+)?' => 2000, - 'xsd(\d+)?' => 3000, - '[^vx]high(\d+)?' => 4000, - 'xhigh(\d+)?' => 5000, - '[^x]std(\d+)?' => 6000, - 'xstd(\d+)?' => 7000, - 'med(\d+)?' => 8000, - 'low(\d+)?' => 9000, - 'subtitles(\d+)?' => 10000, + 'fhd(\d+)?' => 1000, + '(\b|[^f])hd(\d+)?' => 2000, + '(\b|[^x])sd(\d+)?' => 3000, + 'xsd(\d+)?' => 4000, + '(\b|[^x])web(\d+)?' => 5000, + 'xweb(\d+)?' => 6000, + 'mobile(\d+)?' => 7000, + 'high(\d+)?' => 8000, + 'std(\d+)?' => 9000, + 'med(\d+)?' => 10000, + 'low(\d+)?' => 11000, + 'subtitles(\d+)?' => 12000, ); my %ranks2 = ( - '^haf' => 10, - '^hla' => 20, - '^hvf' => 30, - '^hls' => 40, - '^daf' => 50, - '^dvf' => 60, - '^subtitle' => 70, + '^hls' => 10, + '^dash' => 20, + '^subtitle' => 30, ); my ($rank_x, $rank_y); for my $k ( keys %ranks ) { @@ -3807,10 +3724,6 @@ main::logger "WARNING: A UK TV licence is required to access BBC iPlayer TV content legally\n"; } - # Get all possible (or user overridden) modes for this prog recording - my $modelist = $prog->modelist(); - main::logger "INFO: Mode list: $modelist\n" if $opt->{verbose}; - ######## version loop ####### # Do this for each version tried in this order (if they appeared in the content) for my $version ( @version_search_list ) { @@ -3822,6 +3735,7 @@ main::logger "INFO: Regenerate filename for version change: $prog->{version} -> $version\n" if ( $prog->{version} && $opt->{verbose} ); } $prog->{version} = $version; + $prog->{verpid} = $prog->{verpids}->{$version}; main::logger "INFO: Found version: '$prog->{version}'\n" if $opt->{verbose}; # Try to get stream data for this version if not already populated @@ -3829,6 +3743,10 @@ $prog->{streams}->{$version} = $prog->get_stream_data( $prog->{verpids}->{$version}, undef, $version ); } + # Get all possible (or user overridden) modes for this prog version + my $modelist = $prog->modelist(); + main::logger "INFO: Mode list for version '$prog->{version}': $modelist\n" if $opt->{verbose}; + ########## mode loop ######## # record prog depending on the prog type @@ -3836,7 +3754,7 @@ my @modes; my @available_modes = sort keys %{ $prog->{streams}->{$version} }; for my $modename ( split /,/, $modelist ) { - next if $opt->{audioonly} && $prog->{type} eq "tv" && $modename =~ /^hls/; + # next if $opt->{audioonly} && $prog->{type} eq "tv" && $modename =~ /^hls/; # find all numbered modes starting with this modename push @modes, sort { $a cmp $b } grep /^$modename(\d+)?$/, @available_modes; } @@ -3848,20 +3766,21 @@ my %available_modes_short; # Strip the number from the end of the mode name and make a unique array for my $modename ( @available_modes ) { - next if $opt->{audioonly} && $prog->{type} eq "tv" && $modename =~ /^hls/; + # next if $opt->{audioonly} && $prog->{type} eq "tv" && $modename =~ /^hls/; next if $modename =~ /subtitle/; $modename =~ s/\d+$//g; $available_modes_short{$modename}++; } - my $msg = "No supported modes"; + my $msg = "No supported recording quality"; if ( $opt->{$prog->{type}."mode"} || $opt->{modes} ) { - $msg = "No specified modes"; + $msg = "No specified recording quality"; } + my $available_qualities = $prog->qualities_from_modes( join( ",", sort Programme::cmp_modes @available_modes ) ); main::logger "INFO: $msg ".($modelist ? "($modelist) " : "")."available for this programme with version '$version'\n"; - if ( keys %available_modes_short ) { - main::logger "INFO: Available modes: ".(join ',', sort Programme::cmp_modes keys %available_modes_short)."\n"; + if ( $available_qualities ) { + main::logger "INFO: Available qualities: $available_qualities\n" } else { - main::logger "INFO: No other modes are available\n"; + main::logger "INFO: No other recording quality is available\n"; main::logger "INFO: The programme may no longer be available - check the iPlayer or Sounds site\n"; main::logger "INFO: The programme may only be available in an unsupported format (e.g., Flash) - check the iPlayer or Sounds site\n"; main::logger "INFO: If you use a VPN/VPS/Smart DNS/web proxy, it may have been blocked\n"; @@ -3881,6 +3800,7 @@ $prog->{mode} = $mode; # Keep short mode name for substitutions $prog->{modeshort} = $modeshort; + $prog->{quality} = $prog->qualities_from_modes($mode); # try the recording for this mode (rtn==0 -> success, rtn==1 -> next mode, rtn==2 -> next prog) $retcode = mode_ver_download_retry_loop( $prog, $hist, $ua, $mode, $version, $prog->{verpids}->{$version} ); @@ -3992,9 +3912,9 @@ $prog->{streams}->{$version} = $prog->get_stream_data( $version_pid, undef, $version ); if ( keys %{ $prog->{streams}->{$version} } == 0 ) { main::logger "WARNING: No streams available for '$version' version ($prog->{verpids}->{$version}) - skipping (retry)\n"; - if ( $prog->{geoblocked} ) { + if ( $prog->{geoblocked}->{$version} ) { main::logger "WARNING: The BBC blocked access to this programme because it determined that you are outside the UK. (retry)\n"; - } elsif ( $prog->{unavailable} ) { + } elsif ( $prog->{unavailable}->{$version} ) { main::logger "WARNING: The BBC lists this programme as unavailable - check the iPlayer or Sounds site (retry)\n"; } return 2; @@ -4054,18 +3974,10 @@ $template->{generic} .= '<program_meta_data xmlns="http://linuxcentre.net/xmlstuff/get_iplayer" revision="1">'."\n"; my $version = $prog->{version} || 'unknown'; for my $key ( sort keys %{$prog} ) { + next if $key eq "versions"; my $subst = "[$key]"; my $value = $prog->{$key}; - if ( ref$value eq 'HASH' ) { - if ( ref$value->{$version} ne 'HASH' ) { - # abbreviate mode lists - if ( $key eq "modes" || $key eq "modesizes" ) { - my %seen = (); - my @vals = sort Programme::cmp_modes grep { not $seen{$_}++ } map { $_ =~ s/([a-z])\d+/$1/; $_; } split(",", $value->{$version}); - $subst = join(",", @vals); - } - } - } + next if ( ref$value eq 'HASH' ); $template->{generic} .= "\t<$key>$subst</$key>\n"; } $template->{generic} .= "</program_meta_data>\n"; @@ -4113,22 +4025,9 @@ $self->{runtime} = int($self->{duration} / 60); my $jom = {}; for my $key ( sort keys %{$self} ) { + next if $key eq "versions"; my $value = $self->{$key}; - # Get version specific value if this key is a hash - if ( ref$value eq 'HASH' ) { - if ( ref$value->{$version} ne 'HASH' ) { - # abbreviate mode lists - if ( $key eq "modes" || $key eq "modesizes" ) { - my %seen = (); - my @vals = sort Programme::cmp_modes grep { not $seen{$_}++ } map { $_ =~ s/([a-z])\d+/$1/; $_; } split(",", $value->{$version}); - $value = join(",", @vals); - } else { - $value = $value->{$version}; - } - } else { - next; - } - } + next if ( ref$value eq 'HASH' ); # Join array elements if value is ARRAY type if ( ref$value eq 'ARRAY' ) { $value = join ',', @{ $value }; @@ -4310,11 +4209,11 @@ $prog->{ext} = 'EXT' if ! $prog->{ext}; # output files with --raw if ( $opt->{raw} && $mode ) { - if ( $mode =~ /(haf|hvf|hls)/ ) { + if ( $mode =~ /^hls/ ) { $prog->{ext} = "ts"; - } elsif ( $mode =~ /daf/ ) { + } elsif ( $mode =~ /^dash/ && $prog->{type} eq "radio" ) { $prog->{ext} = "raw.m4a"; - } elsif ( $mode =~ /dvf/ ) { + } elsif ( $mode =~ /^dash/ && $prog->{type} eq "tv" ) { $prog->{rawaudio} = main::encode_fs(File::Spec->catfile($prog->{dir}, "$prog->{fileprefix}.raw.m4a")); $prog->{rawvideo} = main::encode_fs(File::Spec->catfile($prog->{dir}, "$prog->{fileprefix}.raw.m4v")); } @@ -5507,9 +5406,12 @@ my $modes; my $mode_sizes; + my $qualities; + my $quality_sizes; for my $version ( sort keys %{ $prog->{verpids} } ) { my @version_modes = sort Programme::cmp_modes keys %{ $prog->{streams}->{$version} }; $modes->{$version} = join ',', @version_modes; + $qualities->{$version} = $prog->qualities_from_modes($modes->{$version}); # Estimate the file sizes for each mode my @sizes; for my $mode ( @version_modes ) { @@ -5531,18 +5433,21 @@ } } $mode_sizes->{$version} = join ',', sort Programme::cmp_modes @sizes; + $quality_sizes->{$version} = $prog->qualitysizes_from_modesizes($mode_sizes->{$version}); } $prog->{versions} = $versions; $prog->{modes} = $modes; $prog->{modesizes} = $mode_sizes; + $prog->{qualities} = $qualities; + $prog->{qualitysizes} = $quality_sizes; # check at least one version available if ( keys %{ $prog->{verpids} } == 0 ) { main::logger "WARNING: No media streams found for requested programme versions and recording modes.\n"; - if ( $prog->{geoblocked} ) { + if ( $prog->{geoblocked}->{$version} ) { main::logger "WARNING: The BBC blocked access to this programme because it determined that you are outside the UK.\n"; - } elsif ( $prog->{unavailable} ) { + } elsif ( $prog->{unavailable}->{$version} ) { main::logger "WARNING: The BBC lists this programme as unavailable - check the iPlayer or Sounds site.\n"; } else { main::logger "WARNING: The programme may no longer be available - check the iPlayer or Sounds site.\n"; @@ -6108,14 +6013,14 @@ } $conn->{href} = $manifest_url; my $xml = main::request_url_retry( $ua, $conn->{href}, 3, undef, undef, 1, undef, 1 ); - if ( ! $xml ) { + if ( $xml !~ /<\?xml.*?<MPD/si ) { main::logger "WARNING: No DASH manifest returned ($conn->{href})\n" if $opt->{verbose}; return; } my $dom; eval { $dom = XML::LibXML->load_xml(string => $xml); }; if ( $@ ) { - main::logger "ERROR: Failed to load DASH manifest:\n$@"; + main::logger "ERROR: Failed to load DASH manifest:\n$@" if $opt->{verbose}; return; } my $xpc = XML::LibXML::XPathContext->new($dom); @@ -6370,7 +6275,6 @@ # $media = undef|<modename> sub get_stream_data { my ( $prog, $verpid, $media, $version ) = @_; - my $modelist = $prog->modelist(); my $data = {}; main::logger "INFO: Getting stream data for version: '$version'\n" if $opt->{verbose}; @@ -6388,6 +6292,16 @@ $exclude_regex = '_('.(join('|', @exclude_supplier)).')'; } + # filter formats + my @exclude_format = split(/,/, $opt->{excludeformat}); + if ( $opt->{includeformat} ) { + @exclude_format = grep { $opt->{includeformat} !~ /\b$_\b/ } @exclude_format; + } + my $exclude_format; + if ( @exclude_format ) { + $exclude_format = join(',', @exclude_format); + } + # retrieve stream data my $ua = main::create_ua( 'desktop' ); my $unblocked; @@ -6398,9 +6312,8 @@ my @medias; my @mediasets; my @ms_tf; - my $unknown_modes = $modelist !~ /(daf|dvf|haf|hla|hvf)/; - my $get_dash = $opt->{info} || $modelist =~ /(daf|dvf)/ || $unknown_modes; - my $get_hls = $opt->{info} || $modelist =~ /(haf|hla|hvf)/ || $unknown_modes; + my $get_dash = $opt->{info} || $exclude_format !~ /dash/; + my $get_hls = $opt->{info} || $exclude_format !~ /hls/; if ( $get_dash ) { push @ms_tf, "dash"; push @mediasets, "iptv-all", "pc"; @@ -6483,12 +6396,12 @@ } unless ( $unblocked ) { - $prog->{geoblocked} = 1 if $checked_geoblock; + $prog->{geoblocked}->{$version} = 1 if $checked_geoblock; return undef; } unless ( $isavailable ) { - $prog->{unavailable} = 1 if $checked_unavailable; + $prog->{unavailable}->{$version} = 1 if $checked_unavailable; return undef; } @@ -6497,95 +6410,47 @@ # Put verpid into mattribs $mattribs->{verpid} = $verpid; - $mattribs->{modelist} = $modelist; - if ( $mattribs->{service} =~ /hla/ ) { - if ( $mattribs->{kind} =~ 'audio' ) { - my $ext = "m4a"; - if ( $mattribs->{bitrate} >= 192 ) { - get_stream_data_cdn( $data, $mattribs, 'hlahigh', 'hls', $ext ); - } elsif ( $mattribs->{bitrate} >= 120 ) { - get_stream_data_cdn( $data, $mattribs, 'hlastd', 'hls', $ext ); - } elsif ( $mattribs->{bitrate} >= 80 ) { - get_stream_data_cdn( $data, $mattribs, 'hlamed', 'hls', $ext ); - } else { - get_stream_data_cdn( $data, $mattribs, 'hlalow', 'hls', $ext ); - } + if ( $mattribs->{service} =~ /(daf|haf|hla)/ && $mattribs->{kind} =~ 'audio' ) { + my $stm = $mattribs->{service} =~ /daf/ ? "dash" : "hls"; + my $ext = "m4a"; + if ( $mattribs->{bitrate} >= 192 ) { + get_stream_data_cdn( $data, $mattribs, "${stm}high", $stm, $ext ); + } elsif ( $mattribs->{bitrate} >= 120 ) { + get_stream_data_cdn( $data, $mattribs, "${stm}std", $stm, $ext ); + } elsif ( $mattribs->{bitrate} >= 80 ) { + get_stream_data_cdn( $data, $mattribs, "${stm}med", $stm, $ext ); + } else { + get_stream_data_cdn( $data, $mattribs, "${stm}low", $stm, $ext ); } - } elsif ( $mattribs->{service} =~ /hvf/ ) { - if ( $mattribs->{kind} =~ 'video' ) { - my $ext = "mp4"; - if ( $mattribs->{height} > 1000 ) { - # full HD streams do not exist yet - } elsif ( $mattribs->{height} > 700 ) { - get_stream_data_cdn( $data, $mattribs, "hvfhd", 'hls', $ext ); - } elsif ( $mattribs->{height} > 500 ) { - if ( $mattribs->{fps} > 25 ) { - get_stream_data_cdn( $data, $mattribs, "hvfsd", 'hls', $ext ); + } elsif ( $mattribs->{service} =~ /(dvf|hvf|hls)/ && $mattribs->{kind} =~ 'video' ) { + my $stm = $mattribs->{service} =~ /dvf/ ? "dash" : "hls"; + my $ext = "mp4"; + if ( $mattribs->{height} > 700 ) { + get_stream_data_cdn( $data, $mattribs, "${stm}hd", $stm, $ext ); + } elsif ( $mattribs->{height} > 500 ) { + if ( $mattribs->{fps} > 25 ) { + if ( $mattribs->{bitrate} >= 2500 ) { + get_stream_data_cdn( $data, $mattribs, "${stm}sd", $stm, $ext ); } else { - get_stream_data_cdn( $data, $mattribs, "hvfxsd", 'hls', $ext ); + get_stream_data_cdn( $data, $mattribs, "${stm}xsd", $stm, $ext ); } - } elsif ( $mattribs->{height} > 360 ) { - if ( $mattribs->{fps} > 25 ) { - get_stream_data_cdn( $data, $mattribs, "hvfhigh", 'hls', $ext ); - } else { - get_stream_data_cdn( $data, $mattribs, "hvfxhigh", 'hls', $ext ); - } - } elsif ( $mattribs->{height} > 260 && $mattribs->{height} < 300 ) { - get_stream_data_cdn( $data, $mattribs, "hvflow", 'hls', $ext ); - } - } - - } elsif ( $mattribs->{service} =~ /haf/ ) { - if ( $mattribs->{kind} =~ 'audio' ) { - my $ext = "m4a"; - if ( $mattribs->{bitrate} >= 192 ) { - get_stream_data_cdn( $data, $mattribs, 'hafhigh', 'hls', $ext ); - } elsif ( $mattribs->{bitrate} >= 120 ) { - get_stream_data_cdn( $data, $mattribs, 'hafstd', 'hls', $ext ); - } elsif ( $mattribs->{bitrate} >= 80 ) { - get_stream_data_cdn( $data, $mattribs, 'hafmed', 'hls', $ext ); } else { - get_stream_data_cdn( $data, $mattribs, 'haflow', 'hls', $ext ); + get_stream_data_cdn( $data, $mattribs, "${stm}xsd", $stm, $ext ); } - } - - } elsif ( $mattribs->{service} =~ /dvf/ ) { - if ( $mattribs->{kind} =~ 'video' ) { - my $ext = "mp4"; - if ( $mattribs->{height} > 700 ) { - get_stream_data_cdn( $data, $mattribs, "dvfhd", 'dash', $ext ); - } elsif ( $mattribs->{height} > 500 ) { - if ( $mattribs->{bitrate} > 2500 ) { - get_stream_data_cdn( $data, $mattribs, "dvfsd", 'dash', $ext ); + } elsif ( $mattribs->{height} > 350 ) { + if ( $mattribs->{fps} > 25 ) { + if ( $mattribs->{bitrate} >= 1250 ) { + get_stream_data_cdn( $data, $mattribs, "${stm}web", $stm, $ext ); } else { - get_stream_data_cdn( $data, $mattribs, "dvfxsd", 'dash', $ext ); + get_stream_data_cdn( $data, $mattribs, "${stm}xweb", $stm, $ext ); } - } elsif ( $mattribs->{height} > 350 ) { - if ( $mattribs->{bitrate} > 1500 ) { - get_stream_data_cdn( $data, $mattribs, "dvfhigh", 'dash', $ext ); - } else { - get_stream_data_cdn( $data, $mattribs, "dvfxhigh", 'dash', $ext ); - } - } elsif ( $mattribs->{height} > 250 ) { - get_stream_data_cdn( $data, $mattribs, "dvflow", 'dash', $ext ); - } - } - - } elsif ( $mattribs->{service} =~ /daf/ ) { - if ( $mattribs->{kind} =~ 'audio' ) { - my $ext = "m4a"; - # use DASH 320k stream as HLS 320k stream - if ( $mattribs->{bitrate} >= 192 ) { - get_stream_data_cdn( $data, $mattribs, "dafhigh", 'dash', $ext ); - } elsif ( $mattribs->{bitrate} >= 120 ) { - get_stream_data_cdn( $data, $mattribs, "dafstd", 'dash', $ext ); - } elsif ( $mattribs->{bitrate} >= 80 ) { - get_stream_data_cdn( $data, $mattribs, "dafmed", 'dash', $ext ); } else { - get_stream_data_cdn( $data, $mattribs, "daflow", 'dash', $ext ); + get_stream_data_cdn( $data, $mattribs, "${stm}xweb", $stm, $ext ); } + } elsif ( $mattribs->{height} > 250 ) { + get_stream_data_cdn( $data, $mattribs, "${stm}mobile", $stm, $ext ); } # Subtitles modes @@ -6619,6 +6484,37 @@ } } + # generate FHD streams + for my $key (keys %{$data}) { + if ( $key =~ m/(hls|dash)hd/ ) { + my $xvi = 12000; + my $xvb = 8490; + my $xvw = 1920; + my $xvh = 1080; + my $xvr = 50; + my $stm2 = dclone($data->{$key}); + (my $key2 = $key) =~ s/(hls|dash)hd/${1}fhd/; + $stm2->{streamurl} =~ s/video=\d+/video=${xvi}000/; + $stm2->{video_bitrate} = $xvb; + $stm2->{bitrate} = $stm2->{video_bitrate} + $stm2->{audio_bitrate}; + $stm2->{type} =~ s/^(.*?gip_.vf_)\d+(.*?)\d+x\d+(.*?)\d+(fps.*?)\d+(kbps.*)$/$1${xvb}$2${xvw}x${xvh}$3${xvr}$4${xvb}$5/; + $stm2->{type} = sprintf("%12s %4s %4s %9s %5s %8s %7s %s", split(" ", $stm2->{type})); + if ( $key2 =~ /dashfhd/ ) { + $stm2->{size} = int($stm2->{audio_media}->{programme_duration} * $stm2->{bitrate} * 1000.0 / 8.0); + $stm2->{video_media}->{height} = $xvh; + $stm2->{video_media}->{width} = $xvw; + $stm2->{video_media}->{bitrate} = $xvb; + $stm2->{video_media}->{file_size} = int($stm2->{video_media}->{programme_duration} * $stm2->{video_media}->{bitrate} * 1000.0 / 8.0); + $stm2->{video_media}->{service} =~ s/(gip_dvf_)\d+/$1${xvb}/; + $stm2->{video_media}->{id} = "video=${xvi}000"; + } + my $xvs = main::request_url_retry( $ua, $stm2->{streamurl}, 3, undef, undef, 1, undef, 1 ); + if ( $xvs && $xvs !~ /<html/i ) { + $data->{$key2} = $stm2; + } + } + } + # Report modes found if ( $opt->{verbose} ) { main::logger sprintf("INFO: Found mode %10s: %s\n", $_, $data->{$_}->{type}) for sort Programme::cmp_modes keys %{ $data }; @@ -6636,165 +6532,153 @@ sub modelist { my $prog = shift; + my $regex_quality = qr/(fhd|hd|sd|web|mobile|high|std|med|low|default)/; + my $regex_alias = qr/(1080p?|720p?|540p?|396p?|288p?|320k?|128k?|96k?|48k?)/; + my $replace_alias = { + "1080p" => "fhd", + "720p" => "hd", + "540p" => "sd", + "396p" => "web", + "288p" => "mobile", + "320k" => "high", + "128k" => "std", + "96k" => "med", + "48k" => "low", + "1080" => "fhd", + "720" => "hd", + "540" => "sd", + "396" => "web", + "288" => "mobile", + "320" => "high", + "128" => "std", + "96" => "med", + "48" => "low", + }; + my $tvlbr = $opt->{fps25}; my $mlist = $opt->{$prog->{type}."mode"} || $opt->{modes}; - # Defaults - if ( ! $mlist ) { - $mlist = 'default'; - } + $mlist =~ s/\s+//g; + $mlist =~ s/,{2,}/,/g; + $mlist = lc($mlist); my $mlist_orig = $mlist; # backcompat - $mlist =~ s/(\b|[^t])vgood/$1better/g; + # prefixes first + $mlist =~ s/(flash|hls)aac//g; + $mlist =~ s/(flash|rtmp)//g; + $mlist =~ s/(dash|dvf|daf)//g; + $mlist =~ s/(hls|hvf|haf|hla)//g; + $mlist =~ s/(tv|radio)//g; + # then suffixes + $mlist =~ s/([^,\d]+)\d+/$1/g; + $mlist =~ s/vgood/better/g; $mlist =~ s/worse/good/g; - $mlist =~ s/hlsvhigh/hvfxsd/g; - $mlist =~ s/(hlsx?|hvf)std/hvfxhigh/g; - $mlist =~ s/(flash|hls)aac/radio/g; - $mlist =~ s/(flash|rtmp)/tv/g; - if ( $mlist ne $mlist_orig && ! $opt->{nowarnmoderemap} ) { - main::logger "WARNING: Invalid mode list '$mlist_orig' remapped to '$mlist'\n"; - main::logger "WARNING: Please update your preferences\n"; - $opt->{nowarnmoderemap} = 1; + $mlist =~ s/vhigh/sd/g; + $mlist =~ s/x(high|std)/xweb/g; + if ( $prog->{type} eq "tv" ) { + $mlist =~ s/high/web/g; + $mlist =~ s/low/mobile/g; } - # stream format aliases + # then expansions if ( $prog->{type} eq "tv" ) { - $mlist =~ s/dash/dvf/g; - $mlist =~ s/hls(?!hd)/hvf/g; + $mlist =~ s/best/hd,sd,web,mobile/g; + $mlist =~ s/better/sd,web,mobile/g; + $mlist =~ s/good/web,mobile/g; + $mlist =~ s/worst/mobile/g; } elsif ( $prog->{type} eq "radio" ) { - $mlist =~ s/dash/daf/g; - $mlist =~ s/hls(?!hd)/hlsaudio/g; + $mlist =~ s/best/high,std,med,low/g; + $mlist =~ s/better/std,med,low/g; + $mlist =~ s/good/med,low/g; + $mlist =~ s/worst/low/g; } - # Deal with fallback modes and expansions - # Generic aliases - $mlist = main::expand_list($mlist, 'default', "$prog->{type}default"); - $mlist = main::expand_list($mlist, 'best', "$prog->{type}best"); - $mlist = main::expand_list($mlist, 'better', "$prog->{type}better"); - $mlist = main::expand_list($mlist, 'good', "$prog->{type}good"); - $mlist = main::expand_list($mlist, 'worst', "$prog->{type}worst"); - # single quality levels - if ( $prog->{type} eq "tv" ) { - $mlist = main::expand_list($mlist, 'hd', "$prog->{type}hd"); - $mlist = main::expand_list($mlist, 'sd', "$prog->{type}sd"); + $mlist = join( ",", grep( /^(${regex_quality}|${regex_alias})$/, split( /,/, $mlist ) ) ); + if ( $mlist ne $mlist_orig && ! $opt->{nowarnmoderemap} ) { + main::logger "WARNING: Recording quality settings '$mlist_orig' corrected to '$mlist'\n"; + main::logger "WARNING: Please update your preferences, preset, or PVR search\n"; + $opt->{nowarnmoderemap} = 1; } - $mlist = main::expand_list($mlist, 'high', "$prog->{type}high"); - if ( $prog->{type} eq "radio" ) { - $mlist = main::expand_list($mlist, 'std', "$prog->{type}std"); - $mlist = main::expand_list($mlist, 'med', "$prog->{type}med"); - } - $mlist = main::expand_list($mlist, 'low', "$prog->{type}low"); - # DASH on-demand radio - if ( $prog->{type} eq "radio" && $mlist =~ /daf/ ) { - $mlist = main::expand_list($mlist, 'daf', 'dafdefault'); - $mlist = main::expand_list($mlist, 'dafdefault', 'dafbest'); - $mlist = main::expand_list($mlist, 'dafbest', 'dafhigh,dafbetter'); - $mlist = main::expand_list($mlist, 'dafbetter', 'dafstd,dafgood'); - $mlist = main::expand_list($mlist, 'dafgood', 'dafmed,dafworst'); - $mlist = main::expand_list($mlist, 'dafworst', 'daflow'); - } - # DASH on-demand tv - if ( $prog->{type} eq "tv" && $mlist =~ /dvf/ ) { - $mlist = main::expand_list($mlist, 'dvf', 'dvfdefault'); - $mlist = main::expand_list($mlist, 'dvfdefault', 'dvfbest'); - if ( $opt->{fps25} ) { - $mlist = main::expand_list($mlist, 'dvfbest', 'dvfbetter'); - $mlist = main::expand_list($mlist, 'dvfbetter', 'dvfxsd,dvfgood'); - $mlist = main::expand_list($mlist, 'dvfgood', 'dvfxhigh,dvfworst'); - } else { - $mlist = main::expand_list($mlist, 'dvfbest', 'dvfhd,dvfbetter'); - $mlist = main::expand_list($mlist, 'dvfbetter', 'dvfsd,dvfxsd,dvfgood'); - $mlist = main::expand_list($mlist, 'dvfgood', 'dvfhigh,dvfxhigh,dvfworst'); - } - $mlist = main::expand_list($mlist, 'dvfworst', 'dvflow'); - } - # HLS Audio Factory on-demand radio - if ( $prog->{type} eq "radio" && $mlist =~ /haf/ ) { - $mlist = main::expand_list($mlist, 'haf', 'hafdefault'); - $mlist = main::expand_list($mlist, 'hafdefault', 'hafbest'); - $mlist = main::expand_list($mlist, 'hafbest', 'hafhigh,hafbetter'); - $mlist = main::expand_list($mlist, 'hafbetter', 'hafstd,hafgood'); - $mlist = main::expand_list($mlist, 'hafgood', 'hafmed,hafworst'); - $mlist = main::expand_list($mlist, 'hafworst', 'haflow'); - } - # HLS audio clips and archives - if ( $prog->{type} eq "radio" && $mlist =~ /hla/ ) { - $mlist = main::expand_list($mlist, 'hla', 'hladefault'); - $mlist = main::expand_list($mlist, 'hladefault', 'hlabest'); - $mlist = main::expand_list($mlist, 'hlabest', 'hlahigh,hlabetter'); - $mlist = main::expand_list($mlist, 'hlabetter', 'hlastd,hlagood'); - $mlist = main::expand_list($mlist, 'hlagood', 'hlamed,hlaworst'); - $mlist = main::expand_list($mlist, 'hlaworst', 'hlalow'); - } - # HLS Video Factory on-demand tv - if ( $prog->{type} eq "tv" && $mlist =~ /hvf/ ) { - $mlist = main::expand_list($mlist, 'hvf', 'hvfdefault'); - $mlist = main::expand_list($mlist, 'hvfdefault', 'hvfbest'); - if ( $opt->{fps25} ) { - $mlist = main::expand_list($mlist, 'hvfbest', 'hvfbetter'); - $mlist = main::expand_list($mlist, 'hvfbetter', 'hvfxsd,hvfgood'); - $mlist = main::expand_list($mlist, 'hvfgood', 'hvfxhigh,hvfworst'); - } else { - $mlist = main::expand_list($mlist, 'hvfbest', 'hvfhd,hvfbetter'); - $mlist = main::expand_list($mlist, 'hvfbetter', 'hvfsd,hvfxsd,hvfgood'); - $mlist = main::expand_list($mlist, 'hvfgood', 'hvfhigh,hvfxhigh,hvfworst'); - } - $mlist = main::expand_list($mlist, 'hvfworst', 'hvflow'); - } - # HLS on-demand radio - if ( $prog->{type} eq "radio" && $mlist =~ /hlsaudio/ ) { - $mlist = main::expand_list($mlist, 'hlsaudio', 'hlsaudiodefault'); - $mlist = main::expand_list($mlist, 'hlsaudiodefault', 'hlsaudiobest'); - $mlist = main::expand_list($mlist, 'hlsaudiobest', 'hafhigh,hlahigh,hlsaudiobetter'); - $mlist = main::expand_list($mlist, 'hlsaudiobetter', 'hafstd,hlastd,hlsaudiogood'); - $mlist = main::expand_list($mlist, 'hlsaudiogood', 'hafmed,hlsmed,hlsaudioworst'); - $mlist = main::expand_list($mlist, 'hlsaudioworst', 'haflow,hlalow'); - } - # default on-demand radio - if ( $prog->{type} eq "radio" && $mlist =~ /radio/ ) { - $mlist = main::expand_list($mlist, 'radio', 'radiodefault'); - $mlist = main::expand_list($mlist, 'radiodefault', 'radiobest'); - $mlist = main::expand_list($mlist, 'radiobest', 'hafhigh,hlahigh,dafhigh,radiobetter'); - $mlist = main::expand_list($mlist, 'radiobetter', 'hafstd,hlastd,dafstd,radiogood'); - $mlist = main::expand_list($mlist, 'radiogood', 'hafmed,hlamed,dafmed,radioworst'); - $mlist = main::expand_list($mlist, 'radioworst', 'haflow,hlalow,daflow'); - } - # default on-demand tv - if ( $prog->{type} eq "tv" && $mlist =~ /tv/ ) { - $mlist = main::expand_list($mlist, 'tv', 'tvdefault'); - $mlist = main::expand_list($mlist, 'tvdefault', 'tvbest'); - if ( $opt->{fps25} ) { - $mlist = main::expand_list($mlist, 'tvbest', 'tvbetter'); - $mlist = main::expand_list($mlist, 'tvbetter', 'hvfxsd,dvfxsd,tvgood'); - $mlist = main::expand_list($mlist, 'tvgood', 'hvfxhigh,dvfxhigh,tvworst'); - } else { - $mlist = main::expand_list($mlist, 'tvbest', 'hvfhd,dvfhd,tvbetter'); - $mlist = main::expand_list($mlist, 'tvbetter', 'hvfsd,dvfsd,hvfxsd,dvfxsd,tvgood'); - $mlist = main::expand_list($mlist, 'tvgood', 'hvfhigh,dvfhigh,hvfxhigh,dvfxhigh,tvworst'); - } - $mlist = main::expand_list($mlist, 'tvworst', 'hvflow,dvflow'); - } - # single quality level tv - if ( $prog->{type} eq "tv" && $mlist =~ /\btv(hd|sd|high|low)\b/ ) { - if ( $opt->{fps25} ) { - $mlist = main::expand_list($mlist, 'tvsd', 'hvfxsd,dvfxsd'); - $mlist = main::expand_list($mlist, 'tvhigh', 'hvfxhigh,dvfxhigh'); - } else { - $mlist = main::expand_list($mlist, 'tvhd', 'hvfhd,dvfhd'); - $mlist = main::expand_list($mlist, 'tvsd', 'hvfsd,dvfsd,hvfxsd,dvfxsd'); - $mlist = main::expand_list($mlist, 'tvhigh', 'hvfhigh,dvfhigh,hvfxhigh,dvfxhigh'); - } - $mlist = main::expand_list($mlist, 'tvlow', 'hvflow,dvflow'); - } - # single quality level radio - if ( $prog->{type} eq "radio" && $mlist =~ /\bradio(high|std|med|low)\b/ ) { - $mlist = main::expand_list($mlist, 'radiohigh', 'hafhigh,hlahigh,dafhigh'); - $mlist = main::expand_list($mlist, 'radiostd', 'hafstd,hlastd,dafstd'); - $mlist = main::expand_list($mlist, 'radiomed', 'hafmed,hlamed,dafmed'); - $mlist = main::expand_list($mlist, 'radiolow', 'haflow,hlalow,daflow'); + # Defaults + if ( ! $mlist ) { + $mlist = 'default'; + } + if ( $prog->{type} eq "tv" ) { + if ( $mlist =~ /x(sd|web)/ ) { + $mlist =~ s/x(sd|web)/$1/g; + $tvlbr = 1; + } + if ( $opt->{audioonly} && $prog->{version} eq "audiodescribed" ) { + # for 128k audio + $mlist =~ s/default/hd,web,sd,mobile/g; + } else { + $mlist =~ s/default/hd,sd,web,mobile/g; + } + # ensure valid mode for audiodescribed + if ( $prog->{version} eq "audiodescribed" && $mlist !~ /(sd|web|mobile)/ ) { + if ( $opt->{audioonly} ) { + # for 128k audio + $mlist .= ",web,sd,mobile"; + } else { + $mlist .= ",sd,web,mobile"; + } + } + } elsif ( $prog->{type} eq "radio" ) { + $mlist =~ s/default/high,std,med,low/g; } + $mlist =~ s/${regex_alias}/$replace_alias->{$1}/g; # remove duplicates my %seen; - $mlist = join( ",", grep { my $seen = $seen{$_}; $seen{$_} = 1; !$seen } split( ",", $mlist ) ); + $mlist = join( ",", grep { my $seen = $seen{$_}; $seen{$_} = 1; !$seen } split( /,/, $mlist ) ); + # tv + $mlist = main::expand_list($mlist, 'fhd', "dashfhd,hlsfhd"); + $mlist = main::expand_list($mlist, 'hd', "hlshd,dashhd"); + if ( $tvlbr ) { + $mlist = main::expand_list($mlist, 'sd', "hlsxsd,dashxsd,hlssd,dashsd"); + $mlist = main::expand_list($mlist, 'web', "hlsxweb,dashxweb,hlsweb,dashweb"); + } else { + $mlist = main::expand_list($mlist, 'sd', "hlssd,dashsd,hlsxsd,dashxsd"); + $mlist = main::expand_list($mlist, 'web', "hlsweb,dashweb,hlsxweb,dashxweb"); + } + $mlist = main::expand_list($mlist, 'mobile', "hlsmobile,dashmobile"); + # radio + $mlist = main::expand_list($mlist, 'high', "hlshigh,dashhigh"); + $mlist = main::expand_list($mlist, 'std', "hlsstd,dashstd"); + $mlist = main::expand_list($mlist, 'med', "hlsmed,dashmed"); + $mlist = main::expand_list($mlist, 'low', "hlslow,dashlow"); return $mlist; } +sub qualities_from_modes { + my $prog = shift; + my $modelist = shift; + my $regex_quality = qr/(fhd|hd|sd|web|mobile|high|std|med|low)/; + my @q1 = map { $_ =~ s/^(hls|dash)x?${regex_quality}.*?$/$2/; $_; } split( /,/, $modelist ); + my @q2 = grep(/^${regex_quality}$/, @q1); + my %seen; + my @q3 = grep { my $seen = $seen{$_}; $seen{$_} = 1; !$seen } @q2; + return join(",", @q3); +} + +sub qualitysizes_from_modesizes { + my $prog = shift; + my $modesizes = shift; + my $regex_quality = qr/(fhd|hd|sd|web|mobile|high|std|med|low)/; + my %sizes; + my %units; + my @q1 = map { + $_ =~ m/^(hls|dash)x?${regex_quality}.*?=(\d+)(.*?)$/; + if ( $2 ) { + if ( $3 > $sizes{$2} ) { + $sizes{$2} = $3; + $units{$2} = $4; + } + }; + $2; + } split( /,/, $modesizes ); + my @q2 = grep(/^${regex_quality}$/, @q1); + my @q3 = map { "$_=$sizes{$_}$units{$_}" } @q2; + my %seen; + my @q4 = grep { my $seen = $seen{$_}; $seen{$_} = 1; !$seen } @q3; + return join(",", @q4); +} + sub postproc { my ( $prog, $audio_file, $video_file, $ua ) = @_; my @cmd; @@ -6935,6 +6819,7 @@ 'national' => { 'bbc_one' => 'BBC One', 'bbc_two' => 'BBC Two', + 'bbc_three' => 'BBC Three', 'bbc_four' => 'BBC Four', 'bbc_sport' => 'BBC Sport', 'cbbc' => 'CBBC', @@ -6959,6 +6844,7 @@ 'p00fzl6g' => 'BBC News', # bbcnews/programmes/schedules 'p00fzl6n' => 'BBC One', # bbcone/programmes/schedules/hd 'p00fzl73' => 'BBC Parliament', # bbcparliament/programmes/schedules + 'p00fzl95' => 'BBC Three', # bbcthree/programmes/schedules 'p015pksy' => 'BBC Two', # bbctwo/programmes/schedules/hd 'p00fzl9r' => 'CBBC', # cbbc/programmes/schedules 'p00fzl9s' => 'CBeebies', # cbeebies/programmes/schedules @@ -7003,9 +6889,10 @@ # Class cmdline Options sub opt_format { return { - 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)."], 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."], + fps25 => [ 1, "fps25|tvlbr|tvlowerbitrate|tv-lower-bitrate!", 'Recording', '--tv-lower-bitrate', "Prefer 25fps (or lower-bitrate 50fps) streams for TV programmes if available."], outputtv => [ 1, "outputtv|output-tv=s", 'Output', '--output-tv <dir>', "Output directory for tv recordings (overrides --output)"], + 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"], }; } @@ -7421,6 +7308,9 @@ decode_entities($name); decode_entities($episode); decode_entities($desc); + $name =~ s/\|/-/g; + $episode =~ s/\|/-/g; + $desc =~ s/\|/-/g; $prog->{$pid} = main::progclass($prog_type)->new( 'pid' => $pid, 'name' => $name, @@ -7546,6 +7436,9 @@ decode_entities($name); decode_entities($episode); decode_entities($desc); + $name =~ s/\|/-/g; + $episode =~ s/\|/-/g; + $desc =~ s/\|/-/g; $prog->{$pid} = main::progclass($prog_type)->new( 'pid' => $pid, 'name' => $name, @@ -7571,48 +7464,48 @@ if ( ! $opt->{raw} ) { $prog->ffmpeg_init(); # require ffmpeg for HLS - if ( $mode =~ /^(hls|hvf|haf)/ && ! $opt->{raw} && ! main::exists_in_path('ffmpeg') ) { + if ( $mode =~ /^hls/ && ! $opt->{raw} && ! main::exists_in_path('ffmpeg') ) { main::logger "WARNING: Required ffmpeg utility not found - not converting .ts file(s)\n"; $opt->{raw} = 1; } # cannot convert hvf with avconv or ffmpeg < 2.5 - if ( $mode =~ /^hvf/ && ! $opt->{raw} ) { + if ( $mode =~ /^hls/ && $prog->{type} eq "tv" && ! $opt->{raw} ) { if ( $opt->{myffmpegav} ) { - main::logger "WARNING: avconv does not support conversion of hvf downloads to MP4 - not converting .ts file\n"; + main::logger "WARNING: avconv does not support conversion of HLS downloads to MP4 - not converting .ts file\n"; $opt->{raw} = 1; } elsif ( $opt->{myffmpegxx} ) { - main::logger "WARNING: Unable to determine ffmpeg version - MP4 conversion for hvf downloads may fail\n"; + main::logger "WARNING: Unable to determine ffmpeg version - MP4 conversion for HLS downloads may fail\n"; } elsif ( ! $opt->{myffmpeg25} ) { - main::logger "WARNING: Your version of ffmpeg ($opt->{myffmpegversion}) does not support conversion of hvf downloads to MP4 - not converting .ts file\n"; + main::logger "WARNING: Your version of ffmpeg ($opt->{myffmpegversion}) does not support conversion of HLS downloads to MP4 - not converting .ts file\n"; $opt->{raw} = 1; } if ( $opt->{myffmpegav} || $opt->{myffmpegxx} || ! $opt->{myffmpeg25} ) { - main::logger "WARNING: ffmpeg 2.5 or higher is required to convert hvf downloads to MP4\n"; + main::logger "WARNING: ffmpeg 2.5 or higher is required to convert HLS downloads to MP4\n"; main::logger "WARNING: Use --raw to bypass MP4 conversion and retain .ts file\n"; main::logger "WARNING: Use --ffmpeg-force to override checks and force MP4 conversion attempt\n"; } } # require ffmpeg for DASH - if ( $mode =~ /^(daf|dvf)/ && ( ! $opt->{raw} || $opt->{mpegts} ) && ! main::exists_in_path('ffmpeg') ) { + if ( $mode =~ /^dash/ && ( ! $opt->{raw} || $opt->{mpegts} ) && ! main::exists_in_path('ffmpeg') ) { main::logger "WARNING: Required ffmpeg utility not found - not converting .m4a and .m4v files\n"; $opt->{raw} = 1; delete $opt->{mpegts}; } # cannot convert dvf with avconv or ffmpeg < 3.0 - if ( $mode =~ /^dvf/ && ( ! $opt->{raw} || $opt->{mpegts} ) ) { + if ( $mode =~ /^dash/ && $prog->{type} eq "tv" && ( ! $opt->{raw} || $opt->{mpegts} ) ) { if ( $opt->{myffmpegav} ) { - main::logger "WARNING: avconv does not support conversion of dvf downloads to MPEG-TS/MP4 - not converting .m4a and .m4v files\n"; + main::logger "WARNING: avconv does not support conversion of MPEG-DASH downloads to MPEG-TS/MP4 - not converting .m4a and .m4v files\n"; $opt->{raw} = 1; delete $opt->{mpegts}; } elsif ( $opt->{myffmpegxx} ) { - main::logger "WARNING: Unable to determine ffmpeg version - MPEG-TS/MP4 conversion for dvf downloads may fail\n"; + main::logger "WARNING: Unable to determine ffmpeg version - MPEG-TS/MP4 conversion for MPEG-DASH downloads may fail\n"; } elsif ( ! $opt->{myffmpeg30} ) { - 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"; + 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"; $opt->{raw} = 1; delete $opt->{mpegts}; } if ( $opt->{myffmpegav} || $opt->{myffmpegxx} || ! $opt->{myffmpeg30} ) { - main::logger "WARNING: ffmpeg 3.0 or higher is required to convert dvf downloads to MPEG-TS/MP4\n"; + main::logger "WARNING: ffmpeg 3.0 or higher is required to convert MPEG-DASH downloads to MPEG-TS/MP4\n"; main::logger "WARNING: Use --raw to bypass MPEG-TS/MP4 conversion and retain .m4a and .m4v files\n"; main::logger "WARNING: Use --ffmpeg-force to override checks and force MPEG-TS/MP4 conversion attempt\n"; } @@ -7646,6 +7539,7 @@ if ( ! $opt->{nowrite} ) { # set mode $prog->{mode} = $mode; + $prog->{quality} = $prog->qualities_from_modes($mode); # Disable proxy here if required main::proxy_disable($ua) if $opt->{partialproxy}; @@ -8030,9 +7924,9 @@ # Class cmdline Options sub opt_format { return { - 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)."], 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."], outputradio => [ 1, "outputradio|output-radio=s", 'Output', '--output-radio <dir>', "Output directory for radio recordings (overrides --output)"], + 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."], }; } @@ -9079,6 +8973,8 @@ $opt->{info} = 0; # Do the recording (force --get option) $opt->{get} = 1; + # reset for PVR search + delete $opt->{nowarnmoderemap}; my $failcount = 0; if ( $pvr->{$name}->{pid} ) { @@ -9097,7 +8993,6 @@ } $retcode += $failcount; } - main::purge_warning( $hist, 30 ); return $retcode; } @@ -9173,7 +9068,7 @@ return 1; } # Parse valid options and create array (ignore options from the options files that have not been overriden on the cmdline) - 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} ) { + 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} ) { if ( defined $opt_cmdline->{$_} ) { push @options, "$_ $opt_cmdline->{$_}"; main::logger "DEBUG: Adding option $_ = $opt_cmdline->{$_}\n" if $opt->{debug};
View file
get_iplayer-3.28.tar.gz/get_iplayer.1 -> get_iplayer-3.29.tar.gz/get_iplayer.1
Changed
@@ -1,4 +1,4 @@ -.TH GET_IPLAYER "1" "December 2021" "Phil Lewis" "get_iplayer Manual" +.TH GET_IPLAYER "1" "February 2022" "Phil Lewis" "get_iplayer Manual" .SH NAME get_iplayer \- Stream Recording tool and PVR for BBC iPlayer and BBC Sounds .SH SYNOPSIS @@ -168,10 +168,13 @@ 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. .TP \fB\-\-audio\-only -Only download audio stream for TV programme. 'hls' recording modes are not supported and ignored. Produces .m4a file. Implies \-\-force. +Only download audio stream for TV programme. Produces .m4a file. Implies \-\-force. .TP -\fB\-\-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. +\fB\-\-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. +.TP +\fB\-\-exclude\-format <format>,<format>,... +Comma\-separated list of media stream formats to ignore when recording. Valid values: hls,dash. .TP \fB\-\-exclude\-supplier <supplier>,<supplier>,... Comma\-separated list of media stream suppliers (CDNs) to skip. Possible values: akamai,limelight,bidi,cloudfront. Synonym: \-\-exclude\-cdn. @@ -179,15 +182,15 @@ \fB\-\-force Ignore programme history (unsets \-\-hide option also). .TP -\fB\-\-fps25 -Use only 25fps streams for TV programmes (HD video not available). -.TP \fB\-\-get, \-g Start recording matching programmes. Search terms required. .TP \fB\-\-hash Show recording progress as hashes .TP +\fB\-\-include\-format <format>,<format>,... +Comma\-separated list of media stream to use when recording. Overrides \-\-exclude\-format. Valid values: hls,dash +.TP \fB\-\-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. .TP @@ -197,9 +200,6 @@ \fB\-\-mark\-downloaded Mark programmes in search results or specified with \-\-pid/\-\-url as downloaded by inserting records in download history. .TP -\fB\-\-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). -.TP \fB\-\-no\-merge\-versions Do not merge programme versions with same name and duration. .TP @@ -236,8 +236,11 @@ \fB\-\-proxy, \-p <url> 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. .TP -\fB\-\-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). +\fB\-\-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. +.TP +\fB\-\-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. .TP \fB\-\-start <secs|hh:mm:ss> Recording/streaming start offset (actual start may be several seconds earlier for HLS and DASH streams) @@ -251,8 +254,11 @@ \fB\-\-test, \-t Test only \- no recording (only shows search results with \-\-pvr and \-\-pid\-recursive) .TP -\fB\-\-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). +\fB\-\-tv\-lower\-bitrate +Prefer 25fps (or lower\-bitrate 50fps) streams for TV programmes if available. +.TP +\fB\-\-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 .TP \fB\-\-url <url>,<url>,... Record the PIDs contained in the specified iPlayer episode URLs. Alias for \-\-pid. @@ -422,9 +428,6 @@ \fB\-\-limit\-matches <number> Limits the number of matching results for any search (and for every PVR search) .TP -\fB\-\-nopurge -Don't show warning about programmes recorded over 30 days ago -.TP \fB\-\-prefs\-add Add/Change specified saved user or preset options .TP @@ -563,17 +566,11 @@ \fB\-\-index\-maxconn <number> Maximum number of connections to use for concurrent programme indexing. Default: 5 Min: 1 Max: 10 .TP -\fB\-\-purge\-files -Delete downloaded programmes more than 30 days old -.TP \fB\-\-release\-check Forces check for new release if used on command line. Checks for new release weekly if saved in preferences. .TP \fB\-\-throttle <Mb/s> Bandwidth limit (in Mb/s) for media file download. Default: unlimited. Synonym: \-\-bw -.TP -\fB\-\-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 .SS "Deprecated Options:" .TP \fB\-\-no\-index\-concurrent @@ -583,7 +580,7 @@ .PP This manual page was originally written by Jonathan Wiltshire <jmw@debian.org> for the Debian project (but may be used by others). .SH COPYRIGHT NOTICE -get_iplayer v3.28, Copyright (C) 2008\-2010 Phil Lewis +get_iplayer v3.29, Copyright (C) 2008\-2010 Phil Lewis This program comes with ABSOLUTELY NO WARRANTY; for details use \-\-warranty. This is free software, and you are welcome to redistribute it under certain conditions; use \-\-conditions for details.
View file
get_iplayer-3.28.tar.gz/get_iplayer.cgi -> get_iplayer-3.29.tar.gz/get_iplayer.cgi
Changed
@@ -24,7 +24,7 @@ # License: GPLv3 (see LICENSE.txt) # -my $VERSION = 3.28; +my $VERSION = 3.29; my $VERSION_TEXT; $VERSION_TEXT = sprintf("v%.2f", $VERSION) unless $VERSION_TEXT; @@ -3130,8 +3130,8 @@ }; $opt->{MODES} = { - title => 'Recording Modes', # Title - 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 + title => 'Recording Quality', # Title + tooltip => 'Comma separated list of recording quality settings which should be tried in order', # Tooltip webvar => 'MODES', # webvar optkey => 'modes', # option type => 'text', # type @@ -3259,8 +3259,8 @@ }; $opt->{FPS25} = { - title => 'Use only 25fps streams', - tooltip => "Use only 25fps media streams. HD video not available.", + title => 'Prefer lower-bitrate TV streams', + tooltip => "Prefer lower-bitrate TV streams", webvar => 'FPS25', optkey => 'fps25', type => 'radioboolean',
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.