| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
1
|
|
|
1
|
|
8
|
use v5.37.9; |
|
|
1
|
|
|
|
|
3
|
|
|
2
|
1
|
|
|
1
|
|
3
|
use feature 'class'; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
98
|
|
|
3
|
1
|
|
|
1
|
|
5
|
no warnings 'experimental::class'; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
32
|
|
|
4
|
1
|
|
|
1
|
|
660
|
use open qw( :utf8 :std ); |
|
|
1
|
|
|
|
|
990
|
|
|
|
1
|
|
|
|
|
5
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Video::NRK::Cache::Store 3.02; # Dist::Zilla doesn't know about class yet |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
class Video::NRK::Cache::Store; |
|
9
|
|
|
|
|
|
|
# ABSTRACT: Store NRK Video on Demand cache on disk (abstract base class) |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
1
|
|
|
1
|
|
113
|
use Carp qw( croak ); |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
39
|
|
|
13
|
1
|
|
|
1
|
|
17
|
use Cwd qw( cwd ); |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
39
|
|
|
14
|
1
|
|
|
1
|
|
870
|
use Path::Tiny 0.125 qw( path ); |
|
|
1
|
|
|
|
|
14583
|
|
|
|
1
|
|
|
|
|
77
|
|
|
15
|
1
|
|
|
1
|
|
8
|
use List::Util qw( max ); |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
1255
|
|
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
our $RATE = 1600; # kilo-bytes per second |
|
19
|
|
|
|
|
|
|
our $NICE = 1; |
|
20
|
|
|
|
|
|
|
our $DRY_RUN = 0; |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
field $program_id :param; |
|
24
|
|
|
|
|
|
|
field $url :param; |
|
25
|
|
|
|
|
|
|
field $meta_title :param = $program_id; |
|
26
|
|
|
|
|
|
|
field $meta_desc :param = ''; |
|
27
|
|
|
|
|
|
|
field $options :param = {}; |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
field $dir; |
|
30
|
|
|
|
|
|
|
field $file; |
|
31
|
|
|
|
|
|
|
field $dir_mp4; |
|
32
|
|
|
|
|
|
|
field %dir_sub; |
|
33
|
|
|
|
|
|
|
field $nice = $NICE; |
|
34
|
|
|
|
|
|
|
field $quality = 3; |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# :reader |
|
37
|
|
|
|
|
|
|
method program_id () { $program_id } |
|
38
|
|
|
|
|
|
|
method url () { $url } |
|
39
|
|
|
|
|
|
|
method meta_title () { $meta_title } |
|
40
|
|
|
|
|
|
|
method meta_desc () { $meta_desc } |
|
41
|
|
|
|
|
|
|
method options () { $options } |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
method dir () { $dir } |
|
44
|
|
|
|
|
|
|
method file () { $file } |
|
45
|
|
|
|
|
|
|
method dir_mp4 () { $dir_mp4 } |
|
46
|
|
|
|
|
|
|
method dir_sub () { %dir_sub } |
|
47
|
|
|
|
|
|
|
method nice () { $nice } |
|
48
|
|
|
|
|
|
|
method quality () { $quality } |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
ADJUST { |
|
52
|
|
|
|
|
|
|
$quality = $options->{quality} if defined $options->{quality}; |
|
53
|
|
|
|
|
|
|
$nice = $options->{nice} if defined $options->{nice}; |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
$dir = path(cwd)->child("$meta_title"); |
|
56
|
|
|
|
|
|
|
$file = path(cwd)->child("$meta_title.mp4"); |
|
57
|
|
|
|
|
|
|
$dir_mp4 = $dir->child("$program_id.mp4"); |
|
58
|
|
|
|
|
|
|
$dir_sub{nb_ttv} = $dir->child("$program_id.nb-ttv.vtt"); |
|
59
|
|
|
|
|
|
|
$dir_sub{nb_nor} = $dir->child("$program_id.nb-nor.vtt"); |
|
60
|
|
|
|
|
|
|
$dir_sub{nn_ttv} = $dir->child("$program_id.nn-ttv.vtt"); |
|
61
|
|
|
|
|
|
|
$dir_sub{nn_nor} = $dir->child("$program_id.nn-nor.vtt"); |
|
62
|
|
|
|
|
|
|
$dir_sub{ffmpeg} = $dir->child("$program_id.ffmpeg.vtt"); |
|
63
|
|
|
|
|
|
|
} |
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
method create () { |
|
67
|
|
|
|
|
|
|
$self->prep; |
|
68
|
|
|
|
|
|
|
$self->download; |
|
69
|
|
|
|
|
|
|
$self->ffmpeg; |
|
70
|
|
|
|
|
|
|
$self->post; |
|
71
|
|
|
|
|
|
|
} |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
method rate () { |
|
75
|
|
|
|
|
|
|
return unless $nice; |
|
76
|
|
|
|
|
|
|
return max 1, int $RATE / 2 ** ($nice - 1); |
|
77
|
|
|
|
|
|
|
} |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
method prep () { |
|
81
|
|
|
|
|
|
|
croak "File exists: $file" if $file->exists; |
|
82
|
|
|
|
|
|
|
$dir->mkdir; |
|
83
|
|
|
|
|
|
|
} |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
method ffmpeg () { |
|
87
|
|
|
|
|
|
|
my @codecs = ( |
|
88
|
|
|
|
|
|
|
'-c:v' => $options->{vcodec} ? split ' ', $options->{vcodec} : 'copy', |
|
89
|
|
|
|
|
|
|
'-c:a' => $options->{acodec} ? split ' ', $options->{acodec} : 'copy', |
|
90
|
|
|
|
|
|
|
); |
|
91
|
|
|
|
|
|
|
croak "acodec/vcodec must have uneven number of items" if @codecs & 1; # won't catch the mess if both are wrong |
|
92
|
|
|
|
|
|
|
my $dir_sub = $dir_sub{nb_ttv}->exists ? $dir_sub{nb_ttv} : |
|
93
|
|
|
|
|
|
|
$dir_sub{nn_ttv}->exists ? $dir_sub{nn_ttv} : |
|
94
|
|
|
|
|
|
|
$dir_sub{nb_nor}->exists ? $dir_sub{nb_nor} : |
|
95
|
|
|
|
|
|
|
$dir_sub{nn_nor}->exists ? $dir_sub{nn_nor} : |
|
96
|
|
|
|
|
|
|
undef; |
|
97
|
|
|
|
|
|
|
if ($dir_sub) { |
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# As of 2025, many NRK subs lack a space after . It's a problem |
|
100
|
|
|
|
|
|
|
# with the original data: The NRK web player is affected, too. |
|
101
|
|
|
|
|
|
|
my $sub = path($dir_sub)->slurp_raw; |
|
102
|
|
|
|
|
|
|
$sub =~ s{(?=\w)}{ }g; |
|
103
|
|
|
|
|
|
|
path($dir_sub{ffmpeg})->spew_raw($sub); |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
@codecs = ( |
|
106
|
|
|
|
|
|
|
-f => 'srt', -i => "$dir_sub{ffmpeg}", |
|
107
|
|
|
|
|
|
|
qw( -map 0:0 -map 0:1 -map 1:0 ), @codecs, qw( -c:s mov_text ), |
|
108
|
|
|
|
|
|
|
); |
|
109
|
|
|
|
|
|
|
# https://trac.ffmpeg.org/wiki/Map |
|
110
|
|
|
|
|
|
|
} |
|
111
|
|
|
|
|
|
|
$self->system( 'ffmpeg', |
|
112
|
|
|
|
|
|
|
-i => "$dir_mp4", |
|
113
|
|
|
|
|
|
|
@codecs, |
|
114
|
|
|
|
|
|
|
-metadata => "description=$meta_desc", |
|
115
|
|
|
|
|
|
|
-metadata => "comment=$url", |
|
116
|
|
|
|
|
|
|
-metadata => "copyright=NRK", |
|
117
|
|
|
|
|
|
|
-metadata => "episode_id=$program_id", |
|
118
|
|
|
|
|
|
|
"$file", |
|
119
|
|
|
|
|
|
|
); |
|
120
|
|
|
|
|
|
|
} |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
method post () { |
|
124
|
|
|
|
|
|
|
$dir->remove_tree; |
|
125
|
|
|
|
|
|
|
} |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
method system ($cmd, @args) { |
|
129
|
|
|
|
|
|
|
say join " ", $cmd, @args; |
|
130
|
|
|
|
|
|
|
system $cmd, @args unless $DRY_RUN; |
|
131
|
|
|
|
|
|
|
$self->_ipc_error_check($!, $?, $cmd); |
|
132
|
|
|
|
|
|
|
} |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
method _ipc_error_check ($os_err, $code, $cmd) { |
|
136
|
|
|
|
|
|
|
utf8::decode $os_err; |
|
137
|
|
|
|
|
|
|
croak "$cmd failed to execute: $os_err" if $code == -1; |
|
138
|
|
|
|
|
|
|
croak "$cmd died with signal " . ($code & 0x7f) if $code & 0x7f; |
|
139
|
|
|
|
|
|
|
croak "$cmd exited with status " . ($code >> 8) if $code; |
|
140
|
|
|
|
|
|
|
} |
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
1; |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
__END__ |