#!/usr/bin/perl use strict; use warnings; use DBI; use Digest::SHA qw(sha256_hex); use JSON; use String::ShellQuote qw/shell_quote/; my $db_host = $ENV{'DB_HOST'} || 'mariadb'; my $db_name = $ENV{'DB_NAME'} || 'videodetect'; my $db_user = $ENV{'DB_USER'} || 'videodetect'; my $db_pass = $ENV{'DB_PASSWORD'} || 'videodetect'; $SIG{INT} = sub { die "Interrupted\n"; }; my $dbh = DBI->connect( "DBI:mysql:database=$db_name;host=$db_host;mysql_enable_utf8mb4=1", $db_user, $db_pass, { mysql_auto_reconnect => 1 } ); my $sth = $dbh->prepare("SELECT id, file_path FROM videos ORDER BY id"); my $upd = $dbh->prepare("UPDATE videos SET file_size=?, file_hash=?, resolution_w=?, resolution_h=?, codec=?, duration=? WHERE id=?"); $sth->execute(); while (my($id,$path)=$sth->fetchrow_array()) { print "$path\n"; my $info = get_video_info($path); if ($info) { $upd->execute( -s $path, $info->{file_hash}, $info->{resolution_w}, $info->{resolution_h}, $info->{codec}, $info->{duration}, $id ); } } $sth->finish(); $upd->finish(); $dbh->disconnect(); sub get_video_info { my ($file_path) = @_; # --- Compute SHA-256 hash of the file --- return undef unless -f $file_path && -r $file_path; open(my $fh, '<:raw', $file_path) or do { warn "Cannot open $file_path: $!"; return undef; }; my $hash = Digest::SHA->new(256)->addfile($fh)->hexdigest; close($fh); # --- Run ffprobe to extract metadata --- my $probe_cmd = 'ffprobe -v quiet -print_format json -show_format -show_streams ' . shell_quote($file_path); my $output = `$probe_cmd`; return undef unless defined $output && length($output); my $json = JSON->new->utf8->canonical(1); my $data = $json->decode($output); # --- Extract codec from video stream (prefer first video stream found) --- my $codec = undef; if (exists $data->{streams} && ref($data->{streams}) eq 'ARRAY') { for my $stream (@{$data->{streams}}) { if ($stream->{codec_type} eq 'video') { $codec = $stream->{codec_name}; last; } } } # --- Extract resolution and duration from format/streams --- my ($resolution_w, $resolution_h, $duration); # Duration from format first, then stream if (exists $data->{format} && exists $data->{format}{duration}) { $duration = $data->{format}{duration} + 0; # force numeric } elsif (exists $data->{streams}[0] && exists $data->{streams}[0]{duration}) { $duration = $data->{streams}[0]{duration} + 0; } # Resolution from video stream first, then format side data if (exists $data->{streams} && ref($data->{streams}) eq 'ARRAY') { for my $stream (@{$data->{streams}}) { next unless $stream->{codec_type} eq 'video'; if ($stream->{width} && $stream->{height}) { $resolution_w = $stream->{width}; $resolution_h = $stream->{height}; last; } } } return { file_hash => $hash, resolution_w => $resolution_w // 0, resolution_h => $resolution_h // 0, codec => $codec // 'unknown', duration => $duration, }; }