#!/usr/bin/perl -w

#
# desegcrate.pl, a perl CGI script to download and re-assemble segmented videos
# 
# Rev. 2025-11-14
# 
# Copyright 2025 Christian Wolff
# 
# Redistribution and use in source and binary forms, with or without 
# modification, are permitted provided that the following conditions 
# are met:
# 
# 1. Redistributions of source code must retain the above copyright 
# notice, this list of conditions and the following disclaimer.
# 
# 2. Redistributions in binary form must reproduce the above copyright 
# notice, this list of conditions and the following disclaimer in the 
# documentation and/or other materials provided with the distribution.
# 
# 3. Neither the name of the copyright holder nor the names of its 
# contributors may be used to endorse or promote products derived 
# from this software without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
# “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# 
# https://opensource.org/license/bsd-3-clause
# 
# The author can be reached at dev-desegcrate<at>scara.com; 
# the project's page is at http://git.scara.com/desegcrate/
#

my $wget = 'wget -t 3 -T 10 -N';

#my $useragent = 'Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7.2) Gecko/20040804 Netscape/7.2 (ax)';
my $useragent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:143.0) Gecko/20100101 Firefox/143.0';



### Panopto

# Example URL:
# https://video.hs-mainz.de/Panopto/Pages/Embed.aspx?id=6c3e970b-3e8f-4f5b-8a59-acd200dda957&autoplay=true
# 
# Panopto.Embed.instance = new PanoptoTS.Embed.EmbeddedViewer({"AvailableAudioDescriptionLanguages":[],"AvailableLanguages":[],"CaptionUrl":null,"CopyrightNoticeAutoDismissDuration":0,"CopyrightNoticeText":null,"DeliveryId":"d762d1b6-5bbc-48a8-b9bb-ad1700da8cc1","EnableAudioDescriptions":true,"EnableEmbedMultistream":true,"EnableEmbedMultistreamOverrideTileMode":false,"ErrorCode":0,"FlowplayerHlsSwfPath":"\/Panopto\/Cache\/13.20.3.01092\/Scripts\/FlowPlayer\/7.2.7\/flowplayerhls.swf","FlowplayerSwfPath":"\/Panopto\/Cache\/13.20.3.01092\/Scripts\/FlowPlayer\/7.2.7\/flowplayer.swf","InviteToken":null,"IsAudioOnly":false,"IsLiveBroadcast":false,"LoginUrl":"https:\/\/video.hs-mainz.de\/Panopto\/Pages\/Auth\/Login.aspx?Auth=Viewer&ReturnUrl={0}","LogoUrl":null,"MediaFileType":11,"PlayerKey":"$702841856427673","RtmpRelativeUrl":"m3u8:1ec7b45a-d56d-4b48-8cbb-ad1700da8cb7\/d762d1b6-5bbc-48a8-b9bb-ad1700da8cc1-d2a45efb-4856-4507-878c-ad1700db4538.hls\/master","RtmpServerUrl":"","SessionId":"1ec7b45a-d56d-4b48-8cbb-ad1700da8cb7","ShowCopyrightNotice":false,"ThumbUrl":"https:\/\/panopto-cache.zdv.net\/Panopto\/Content\/Sessions17\/1ec7b45a-d56d-4b48-8cbb-ad1700da8cb7\/b7305d90-f197-427c-8f25-ad1700dae552_et\/thumbs\/slide0.jpg","TrustBrowserNativeHls":false,"VRType":0,"VideoUrl":"https:\/\/panopto-cache.zdv.net\/Panopto\/Content\/Sessions17\/1ec7b45a-d56d-4b48-8cbb-ad1700da8cb7\/d762d1b6-5bbc-48a8-b9bb-ad1700da8cc1-d2a45efb-4856-4507-878c-ad1700db4538.hls\/master.m3u8","ViewerIconUrl":"\/Panopto\/defaultFavicon.ico","ViewerLink":"https:\/\/video.hs-mainz.de\/Panopto\/Pages\/Viewer.aspx?id=d762d1b6-5bbc-48a8-b9bb-ad1700da8cc1","podcastLayout":3}, deliveryApi);
# Panopto.Embed.instance = new PanoptoTS.Embed.EmbeddedViewer(
#{
#	"AvailableAudioDescriptionLanguages":[],
#	"AvailableLanguages":[],
#	"CaptionUrl":null,
#	"CopyrightNoticeAutoDismissDuration":0,
#	"CopyrightNoticeText":null,
#	"DeliveryId":"d762d1b6-5bbc-48a8-b9bb-ad1700da8cc1",
#	"EnableAudioDescriptions":true,
#	"EnableEmbedMultistream":true,
#	"EnableEmbedMultistreamOverrideTileMode":false,
#	"ErrorCode":0,
#	"FlowplayerHlsSwfPath":"\/Panopto\/Cache\/13.20.3.01092\/Scripts\/FlowPlayer\/7.2.7\/flowplayerhls.swf",
#	"FlowplayerSwfPath":"\/Panopto\/Cache\/13.20.3.01092\/Scripts\/FlowPlayer\/7.2.7\/flowplayer.swf",
#	"InviteToken":null,
#	"IsAudioOnly":false,
#	"IsLiveBroadcast":false,
#	"LoginUrl":"https:\/\/video.hs-mainz.de\/Panopto\/Pages\/Auth\/Login.aspx?Auth=Viewer&ReturnUrl={0}",
#	"LogoUrl":null,
#	"MediaFileType":11,
#	"PlayerKey":"$702841856427673",
#	"RtmpRelativeUrl":"m3u8:1ec7b45a-d56d-4b48-8cbb-ad1700da8cb7\/d762d1b6-5bbc-48a8-b9bb-ad1700da8cc1-d2a45efb-4856-4507-878c-ad1700db4538.hls\/master",
#	"RtmpServerUrl":"",
#	"SessionId":"1ec7b45a-d56d-4b48-8cbb-ad1700da8cb7",
#	"ShowCopyrightNotice":false,
#	"ThumbUrl":"https:\/\/panopto-cache.zdv.net\/Panopto\/Content\/Sessions17\/1ec7b45a-d56d-4b48-8cbb-ad1700da8cb7\/b7305d90-f197-427c-8f25-ad1700dae552_et\/thumbs\/slide0.jpg",
#	"TrustBrowserNativeHls":false,
#	"VRType":0,
#	"VideoUrl":"https:\/\/panopto-cache.zdv.net\/Panopto\/Content\/Sessions17\/1ec7b45a-d56d-4b48-8cbb-ad1700da8cb7\/d762d1b6-5bbc-48a8-b9bb-ad1700da8cc1-d2a45efb-4856-4507-878c-ad1700db4538.hls\/master.m3u8",
#	"ViewerIconUrl":"\/Panopto\/defaultFavicon.ico",
#	"ViewerLink":"https:\/\/video.hs-mainz.de\/Panopto\/Pages\/Viewer.aspx?id=d762d1b6-5bbc-48a8-b9bb-ad1700da8cc1",
#	"podcastLayout":3
#}, deliveryApi);


sub download_m3u8_segments {
	my ($path, $index, $base_url, $referer) = @_;
	if (open M3US, "${path}/${index}") {
		while (<M3US>) {
			if (/^\s*([^#]\d+\.ts)/) {
				my $segment = $1;
				print "      Segment: '${segment}'\n";
				my $url = $base_url . $segment;
				`${wget} -q --referer='${referer}' -U '${useragent}' -nd -P '${path}' '${url}' 2>/dev/null`;
			}
		}
		close M3US;
	} else {
		print "    ERROR: Failed to open '${path}/${index}'!\n";
	}
}

sub download_m3u8 {
	my ($path, $name, $base_url, $referer) = @_;
	if (open M3U, "${path}/${name}") {
		while (<M3U>) {
			if (/^\s*([^#].*)\/(index\.m3u8)/) {
				my ($sub, $index) = ($1, $2);
				my $url = $base_url . $sub . '/' . $index;
				# print "    Found index: '${url}'\n";
				print "    Index: '${sub}/${index}'\n";
				`mkdir -p '${path}/${sub}'`;
				`${wget} -q --referer='${referer}' -U '${useragent}' -nd -P '${path}/${sub}' '${url}' 2>/dev/null`;
				if (-s "${path}/${sub}/${index}") {
					download_m3u8_segments(
						$path . '/' . $sub, 
						$index, 
						$base_url . $sub . '/', 
						$referer);
				} else {
					print "    ERROR: Failed to download '${url}' to '${path}/${sub}/${index}'!\n";
				}
			}
		}
		close M3U;
	} else {
		print "  ERROR: Failed to open '${path}/${name}'!\n";
	}
}

sub panopto {
	my ($url) = @_;
	
	my $referer = "https://vangoghtv.hs-mainz.de/";
	#my $id = '6c3e970b-3e8f-4f5b-8a59-acd200dda957';
	#my $url = "https://video.hs-mainz.de/Panopto/Pages/Embed.aspx?id=${id}&autoplay=true";
	
	my $id = '';
	if ($url =~ /Embed\.aspx\?id=(.*?)\&/) {
		$id = $1;
		print "ID: '${id}'\n";
		$url =~s/\&.*$/&autoplay=true/;
	} elsif ($url =~ /Embed\.aspx\?id=(.*$)/) {
		$id = $1;
		print "ID: '${id}'\n";
		$url .= '&autoplay=true';
	} else {
		print "ERROR: No ID in URL: '${url}'\n";
		return 1;
	}
	
	my $path = $id;
	`mkdir -p ${path}`;
	# print "  URL: '${url}'\n";
	if (open PAGE, "${wget} -q --referer='${referer}' -U '${useragent}' -O - '${url}' 2>/dev/null |") {
		while (<PAGE>) {
			if (/"VideoUrl":"(http.*\/)(master\.m3u8)"/) {
				my ($videopath, $name) = ($1, $2);
				$videopath =~s/\\//g;
				my $videourl = $videopath . $name;
				# print "  Found '${videourl}'\n";
				print "  Main: '${name}'\n";
				`${wget} -q --referer='${url}' -U '${useragent}' -nd -P '${path}' '${videourl}' 2>/dev/null`;
				if (-s "${path}/${name}") {
					download_m3u8(
						$path, 
						$name, 
						$videopath, 
						$referer);
				} else {
					print "  ERROR: Failed to download '${videourl}' to '${path}/${name}'!\n";
				}
			}
		}
		close PAGE;
	}
}

my $url;
while ($url = shift) {
	if ($url =~/Panopto/i) {
		panopto($url);
	} else {
		print "Cannot determine player framework.\n";
	}
}

