I was bored and...
These are two scripts: one a recorder and the other a manager. They record online radio. In the first script, you need to paste a link to your radio stream. In the second script manager, in the block responsible for the current song title, you need to:
- paste a link to the endpoint from which you can get the song title.
- parse the song title from the response, either manually or with AI. There's an example with subtitles on YouTube.
Oh, and you'll also need a FAT32 flash drive.
The script manager needs to be run in the scheduler, every 5-60 seconds, depending on how precise you want the song cuts to be.
Recorder Script
:global bassDriveStartFile
:if ([:len [/file find name=$bassDriveStartFile]] > 0) do={
/file remove [find name=$bassDriveStartFile]
}
/tool fetch url="https://chi2.bassdrive.net/stream" dst-path=$bassDriveStartFile
Manager Script
:global bassDriveCurrentShow
:global bassDriveStartFile
:local scriptRecorder "bassDriveRecorder"
:local diskSlot "usb1"
:local folder ($diskSlot . "/bassDrive/")
:local freeSpace (([/disk print as-value where slot=$diskSlot]->0)->"free")
:if ([:len "$freeSpace"] = 0) do={
:log warning ("disk unavailable: " . $diskSlot . " script " . $scriptRecorder . " stopped")
/system script job remove [find script=$scriptRecorder]
:quit
}
:if ($freeSpace < 104857600) do={
:log warning ("low disk space on " . $diskSlot . "script " . $scriptRecorder . " stopped")
/system script job remove [find script=$scriptRecorder]
:quit
}
# start parse song title block
:local rawTitle
:do {
:set rawTitle [/tool fetch url="https://bassdrive.com/now-playing.php" output=user as-value]
} on-error={
:log warning ("get now-playing show failed " . "script " . $scriptRecorder . " stopped")
/system script job remove [find script=$scriptRecorder]
:quit
}
:local html ($rawTitle->"data")
:local spanPos [:find $html "<span"]
:local newShow "Unknown BassDrive Show"
:if ([:len "$spanPos"] > 0) do={
:set newShow [:pick $html 0 $spanPos]
}
# end parse song title block
:local sanitizeShow
:set sanitizeShow do={
:local input $1
:local allowed "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_- "
:local title ""
:for i from=0 to=([:len $input] - 1) do={
:local c [:pick $input $i ($i + 1)]
:if ($allowed ~ $c) do={
:set title ($title . $c)
}
}
:while (([:len $title] > 0) && ([:pick $title ([:len $title] - 1)] = " ")) do={
:set title [:pick $title 0 ([:len $title] - 1)]
}
:return $title
}
:local cleanName [$sanitizeShow $newShow]
#:log info ("bassDrive current show: " . $newShow . "clean: " . $cleanName)
:local date ([/system clock get date] . "/")
:local newFileName ($folder . $date . $cleanName . ".mp3")
:local finalName $newFileName
:local counter 1
:while ([:len [/file find name=$finalName]] > 0) do={
:set finalName ($folder . $date . $cleanName . "_" . $counter . ".mp3")
:set counter ($counter + 1)
}
:if ([:len [/system script job find script=$scriptRecorder]] = 0) do={
:set bassDriveStartFile ($folder . "bassDriveLive.mp3")
:set bassDriveCurrentShow $newShow
/system script run $scriptRecorder
} else={
:if ($bassDriveCurrentShow != $newShow) do={
/system script job remove [find script=$scriptRecorder]
:log info "bassDrive new show started"
:set bassDriveCurrentShow $newShow
/system script run $scriptRecorder
} else={
:if ([:len [/file find name=$bassDriveStartFile]] > 0) do={
/file set name=$finalName $bassDriveStartFile
}
}
}
The scheduler starts the manager, and the manager starts the recorder. To stop recording, stop the scheduler and delete running scripts in Scripts/Jobs.
The result will be something like this
UPD:
Quite by accident, I had to create a script to record a CCTV camera from a free cloud service. Which naturally ended up being a script for IPTV recording.
For example, this channel
#EXTINF:-1 tvg-id="",Gaki no Tsukai (English Subs) (720p)
https://hamada.gaki-no-tsukai.stream/hls/test.m3u8
Answering like this
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:2479
#EXT-X-TARGETDURATION:11
#EXTINF:3.567,
test-2479.ts
#EXTINF:6.033,
test-2480.ts
#EXTINF:8.334,
test-2481.ts
#EXTINF:3.333,
test-2482.ts
You can download using a script. iptvRecorder
:local iptvBaseUrl "https://hamada.gaki-no-tsukai.stream/hls/"
:local iptvListUrl ($iptvBaseUrl . "test.m3u8")
:local diskSlot "usb1"
:local freeSpace (([/disk print as-value where slot=$diskSlot]->0)->"free")
# check disk (100 MB)
:if (([:len "$freeSpace"] = 0) || ($freeSpace < 104857600)) do={
:log warning ("low disk space on " . $diskSlot)
:quit
}
:local getFolderPath do={
:local sDate [/system clock get date]
:local sTime [/system clock get time]
:return ("usb1/iptv_channel/" . [:pick $sDate 0 4] . [:pick $sDate 5 7] . [:pick $sDate 8 10] . "/" . [:pick $sTime 0 2])
}
:local videoDateFolder [$getFolderPath]
:do {
:local playlistContent
:local startPosTsLink
:local endPosTsLink
:local hasChunks true
:local hasChunk true
:local playlistDelay 1
:while ($hasChunks) do={
:delay $playlistDelay
:set playlistContent ([/tool fetch url=$iptvListUrl output=user as-value]->"data")
#:log warning ("playlistContent : " . $playlistContent)
:local searchPosPointer 0
:local startPosTsLink [:find $playlistContent ("test-") $searchPosPointer]
:if ([:len $startPosTsLink] = 0) do={
:log info ("No Links in List")
:set hasChunks false
:set hasChunk false
} else={
:set hasChunk true
}
:local downloadedCount 0
:while ($hasChunk) do={
:local endPosTsLink [:find $playlistContent "\n" $startPosTsLink]
:local segmentFile [:pick $playlistContent $startPosTsLink $endPosTsLink]
#:log info ("Found chunk: " . $segmentFile)
:set videoDateFolder [$getFolderPath]
:local chunkFileLocalPath ($videoDateFolder . "/" . $segmentFile)
:if ([:len [/file find where name=$chunkFileLocalPath]] = 0) do={
:set freeSpace (([/disk print as-value where slot=$diskSlot]->0)->"free")
:if (([:len "$freeSpace"] = 0) || ($freeSpace < 104857600)) do={
:log warning ("low disk space on " . $diskSlot)
:quit
}
:local chunkFileUrl ($iptvBaseUrl . $segmentFile)
:log warning ("Downloading: " . $chunkFileUrl)
:do {
:delay 200ms
/tool fetch url=$chunkFileUrl dst-path=$chunkFileLocalPath keep-result=yes
:set downloadedCount ($downloadedCount + 1)
} on-error={
:log warning ("Failed to download chunk: " . $chunkFileUrl)
}
}
:set searchPosPointer ($endPosTsLink + 1)
:set startPosTsLink [:find $playlistContent ("test-") $searchPosPointer]
:if ([:len $startPosTsLink] = 0) do={
:log info ("End Links")
:set hasChunk false
}
}
:if ($hasChunks) do={
:if ($downloadedCount > 0) do={
:local calculatedDelay (10 - (2 * $downloadedCount))
:if ($calculatedDelay < 2) do={
:set calculatedDelay 200ms
}
:set playlistDelay $calculatedDelay
:log info "HLS_Parser: Downloaded $downloadedCount chunks. Next playlist check in $playlistDelay s."
} else={
:set playlistDelay 10
:log info "HLS_Parser: No NEW chunks in playlist. Cooling down for 10s."
}
}
}
:log info "HLS_Parser: All segments from current playlist verified."
} on-error={
:log error ("HLS_Parser ERROR: ")
}
Running it every 15 seconds
:if ([:len [/system script job find script=iptvRecorder]] = 0) do={
/system script run iptvRecorder
} else={
:log warning "iptvRecorder still running"
}
All downloaded .ts parts can be merged into an mp4 file using ffmpeg.exe with two commands.
copy /b *.ts merged.ts
ffmpeg -i merged.ts -c copy ready_video.mp4
