My question is in regards to combining youtube-dl, ffmpeg, ffplay and PowerShell to handle video URLs.
Some examples I've seen have piped a binary stream from youtube-dl to an external player using the Windows Command Prompt as demonstrated:
youtube-dl --output - "https://youtube.com/mygroovycontent" | mpc-hc.exe /play /close -
This works fine in Command Prompt as it does not mangle the binary stream. If you try and run the same command in PowerShell it doesn't handle the binary stream so well and modifies the output, making it unreadable to the external player.
In light of this I've written the following PowerShell function to get around this issue. It tries to mirror a similar function I've written in Bash (See: https://github.com/adamchilcott/.dotfiles/blob/master/.bash_functions.d/streamer.sh)
The reason I've handled youtube-dl, ffmpeg and ffplay seperately is that defining the ffmpeg binary location in youtube-dl as an external program creates some issues when passing it in PowerShell.
I was hoping that someone could take a look at my script and provide some feedback on what I have done here and if it can be improved upon or if a better implementation is already available?
Best,
Adam.
BEGIN POWERSHELL
Function streamer
{
Param
(
[string] $streamURL
)
Begin
{
}
Process
{
$streamDir = "$env:TEMP\YTD.d"
$ytdBin = "Z:\PortableApps\CommandLineApps\youtube-dl\youtube-dl.exe"
$streamExtractor = &$ytdBin --no-warnings --get-url $streamURL
$ffmpegBin = "Z:\PortableApps\CommandLineApps\ffmpeg-20170702-c885356-win64-static\bin\ffmpeg.exe"
$ffplayBin = "Z:\PortableApps\CommandLineApps\ffmpeg-20170702-c885356-win64-static\bin\ffplay.exe"
if
(
-not (Test-Path -Path $streamDir -PathType Any)
)
{
New-Item $streamDir -type directory -ErrorAction SilentlyContinue
}
Start-Process -FilePath $ffmpegBin -ArgumentList "-loglevel quiet -i $streamExtractor -c copy $streamDir\streamContainer.m2ts" -NoNewWindow -ErrorAction SilentlyContinue
Do
{
Start-Sleep -Seconds 1
}
Until
(
(Get-Item $streamDir\streamContainer.m2ts -ErrorAction SilentlyContinue).Length -gt 256kb
)
&$ffplayBin -loglevel quiet $streamDir\streamContainer.m2ts
if
(
(Test-Path -Path $streamDir -PathType Any) -eq $true -and (Get-Process -Name ffplay -ErrorAction SilentlyContinue) -eq $null
)
{
Do
{
Stop-Process -Name ffmpeg -ErrorAction SilentlyContinue
}
Until
(
(Get-Process -Name ffmpeg -ErrorAction SilentlyContinue) -eq $null
)
Remove-Item $streamDir -Recurse -ErrorAction SilentlyContinue
}
}
End
{
}
}
streamer -streamURL https://www.youtube.com/watch?v=9uFXw7vKz14
END POWERSHELL
Related
I wanted a program that changes my Wallpaper daily, but the "Daily Desktop Wallpaper"-App only can do Full-HD and the official program from Microsoft is not only Adware, but displays an ugly watermark in the bottom right corner and can't change the Lock Screen, so I made my own small script to do that (with some help) that I wanted to share so others don't have to waste their time with this (hence the long title). It uses an API from github.
To do this everyday automatically, put the following action in a Task Scheduler Task that starts daily at a specific time:
Program/script: powershell.exe
Add arguments: -executionPolicy bypass -WindowStyle hidden -File "path\to\changeDesktopToNewestInPicturesPath.ps1"
To the question:
I still have one small problem: How do I change the Lock Screen? The current implementation does not seem to work... (In comments at the end):
Also, any suggestions are very welcome, as I am still pretty new to Powershell.
$dir = "~/Pictures/DailyWallpapers"
if (-not (Test-Path -Path $dir)) {
mkdir $dir
}
$bingApiRequest = Invoke-RestMethod -Uri "https://bing.biturl.top/?resolution=3840" -ContentType "application/json" -Method Get
$fileName = $bingApiRequest.url.split("=")[-1]
Invoke-WebRequest -Uri $bingApiRequest.url -OutFile "~/Pictures/DailyWallpapers/$($fileName)"
$latest = Get-ChildItem -Path $dir | Sort-Object LastAccessTime -Descending | Select-Object -First 1
$filepath = $latest.FullName
$code = #'
using System.Runtime.InteropServices;
namespace Win32{
public class Wallpaper{
[DllImport("user32.dll", CharSet=CharSet.Auto)]
static extern int SystemParametersInfo (int uAction , int uParam , string lpvParam , int fuWinIni) ;
public static void SetWallpaper(string thePath){
SystemParametersInfo(20,0,thePath,3);
}
}
}
'#
add-type $code
#Desktop Wallpaper
[Win32.Wallpaper]::SetWallpaper($filepath)
# $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
# if ($currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
# Write-Host "changing Lock Screen..."
# #Lockscreen
# $regKey = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\PersonalizationCSP'
# if (!(Test-Path -Path $regKey)) {
# $null = New-Item -Path $regKey
# }
# Set-ItemProperty -Path $regKey -Name LockScreenImagePath -value $filepath
# Set-ItemProperty -Path $regKey -Name LockScreenImageUrl -value $filepath
# Set-ItemProperty -Path $regKey -Name LockScreenImageStatus -value 1
# }
like in What is the difference between opening application by cmd/ps or by click? already discussed, I have the problem, that firefox does not use my bookmarks, that I copied with powershell, as long as I open/close firefox within a script.
The only chance I have is: open/close firefox manually and then overwrite bookmarks/passwords with powershell.
Also I created the profile with arguments, but as soon as firefox will be opened, firefox is creating a new profile on its own. Obviously it makes a difference, whether I open firefox by click or ps/cmd.
Does anybody has an idea, how to tell firefox which profile it shall use when the user opens firefox?
Here my code:
# Full path of the file
$appdata = $env:APPDATA
$folder = "$appdata\Custom"
$checkfile = "$folder\firstRunRestore"
#If the file does not exist, create it and execute statement.
if (-not(Test-Path -Path $checkfile -PathType Leaf)) {
try {
$null = New-Item -ItemType File -Path $checkfile -Force -ErrorAction Stop
#Create Firefox profile
$p = New-Object System.Diagnostics.Process
$p.StartInfo.UseShellExecute = $true;
$p.StartInfo.WorkingDirectory = "C:\Program Files\Mozilla Firefox";
$p.StartInfo.Filename = "firefox.exe";
$p.StartInfo.Arguments = "-CreateProfile $env:USERNAME";
$null = $p.Start()
Start-Sleep -s 10
Stop-Process -Name "Firefox"
# Wait to close all processes
Start-Sleep -s 7
# Restore files to profile
$firefox = "$env:APPDATA\Mozilla\Firefox\Profiles"
$curProfile = Get-ChildItem -Path $firefox | Sort-Object LastAccessTime -Descending | Select-Object -First 1
$curPath = "$firefox\$curProfile"
$source = "X:\Desktop\FF-Profile"
$keydb = "key4.db"
$logins = "logins.json"
$places = "places.sqlite"
$favicons = "favicons.sqlite"
Copy-Item -Path "$source\$keydb" -Destination "$curPath" -Force
Copy-Item -Path "$source\$logins" -Destination "$curPath" -Force
Copy-Item -Path "$source\$places" -Destination "$curPath" -Force
Copy-Item -Path "$source\$favicons" -Destination "$curPath" -Force
Remove-Item $source -Force -Recurse
}
catch {
throw $_.Exception.Message
}
}
I have a number of IIS servers, each with a number of sites on them and I want to zip all IIS logs regularly.
I cobbled together the following powershell script with the help of this site and google:
$files = Get-ChildItem "D:\logfiles\IIS-Logs\*.log" -Recurse | Where-Object {($_.LastWriteTime -lt (Get-Date).AddDays(-7))}
foreach ($file in $files) {& 'C:\Program Files\WinRAR\winrar.exe' a -tl -df -m5 "$file.rar" $File}
The problem with this script is that if there are.. say... 2,000 total log files it tried to launch 2,000 simultaneous copies of Winrar and the server will crash. This was unexpected. I expected it to zip the files one at a time. Sequentially.
Does anyone have any ideas to make this work like I want?
I'd really like to use Winrar vs the native Compress-Archive option because:
I want the file dates to reflect the zipped file, not the date it was zipped.
I want the utility to delete the files after archiving because the utility will not delete the file if the archiving failed.
I'm not married to Winrar if I can achieve this another way.
For testing purpose, i created a LogFolder with 3 separate log folders, Log1, Log2 and Log3. Each of these Log folders have 2000 files with 2MB data each. This is the command i ran to Compress each folder `seperately
You can also run these in serial if the performance is too slow (when reading from same disk and writing to same as well).
$ElementsInLog = (Get-ChildItem C:\temp\LogFolder\*.txt -Recurse).Length
$ElementsInLog1 = (Get-ChildItem C:\temp\LogFolder\Log1\*.txt -Recurse).Length
$ElementsInLog2 = (Get-ChildItem C:\temp\LogFolder\Log2\*.txt -Recurse).Length
$ElementsInLog3 = (Get-ChildItem C:\temp\LogFolder\Log3\*.txt -Recurse).Length
Write-Output "Main: $ElementsInLog`nLog1: $ElementsInLog1`nLog2: $elementsInLog2`nLog3: $elementsInLog3"
Write-output "Total File Size: $((Get-ChildItem C:\temp\LogFolder\Log1\*.txt -Recurse | Measure-Object length -Sum).Sum / 1024 / 1024 / 1024) GB"
Write-Output "Starting Tasks..."
$job1 = Start-Job -ScriptBlock {
Write-Output "Log1: $(Get-Date -Format G)"
Get-ChildItem C:\temp\LogFolder\Log1 -Recurse | Compress-Archive -DestinationPath C:\temp\log1.zip -CompressionLevel Fastest
Write-Output "Finished: $(Get-Date -Format G)"
}
$job2 = Start-Job -ScriptBlock {
Write-Output "Log2: $(Get-Date -Format G)"
Get-ChildItem C:\temp\LogFolder\Log3 -Recurse | Compress-Archive -DestinationPath C:\temp\log3.zip -CompressionLevel Fastest
Write-Output "Finished: $(Get-Date -Format G)"
}
$job3 = Start-Job -ScriptBlock {
Write-Output "Log2: $(Get-Date -Format G)"
Get-ChildItem C:\temp\LogFolder\Log4 | Compress-Archive -DestinationPath C:\temp\log4.zip -CompressionLevel Fastest
Write-Output "Finished: $(Get-Date -Format G)"
}
while($job1.State -eq "Running" -or $job2.State -eq "Running" -or $job3.State -eq "Running") {
Start-Sleep 5
}
Receive-Job $job1
Receive-Job $job2
Receive-Job $job3
Output Received
Main: 8000
Log1: 2000
Log2: 2000
Log3: 2000
Total File Size: 4.12791967391968 GB
Starting Tasks...
Log1: 2/10/2020 8:36:22 PM
Finished: 2/10/2020 8:37:30 PM
Log2: 2/10/2020 8:36:22 PM
Finished: 2/10/2020 8:37:27 PM
Log3: 2/10/2020 8:36:22 PM
Finished: 2/10/2020 8:37:28 PM
You probably want to use "Start-Process -Wait" instead of using the &. Using the -Wait flag on Start-Process forces it to wait for completion, and will then cause your program to run sequentially. Check this article on how to use Start-Process: A Better PowerShell Start Process You might also want to use the cmdlet Compress-Archive instead of the command line program Winrar, which might be better integrated and give you better feedback in your scripting.
Something like this with WinRAR?
$files = Get-ChildItem "D:\logfiles\IIS-Logs\*.log" -Recurse | Where-Object {($_.LastWriteTime -lt (Get-Date).AddDays(-7))}
foreach ($file in $files) {
$args = 'a -tl -df -m5 "' + $file.rar + '" ' + $File
Start-Process -Wait -filepath 'C:\Program Files\WinRAR\winrar.exe' -ArgumentList $args
}
Or this with Compress-Archive
$files = Get-ChildItem "D:\logfiles\IIS-Logs\*.log" -Recurse | Where-Object {($_.LastWriteTime -lt (Get-Date).AddDays(-7))}
foreach ($file in $files) {
Compress-Archive -Path $file.FullName -DestinationPath "$($File.FullName).rar"
{
The above are untested, but should work unless I made a typo.
please, could you help me with searching for registry path?
I am trying to find path of REG_BINARY with name 00036601 in HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Profiles\Outlook\20424cec73cea54ab3d011f91bf036b2
I have problem because last folder(20424cec73cea54ab3d011f91bf036b2) in path is different on every laptop. Cannot find any working solution with REG QUERY in cmd or powershell.
I know how to find it in known path or list all subkeys, but failed to filter one value.
So i want to get output like: key name 00036601 found in HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Profiles\Outlook\20424cec73cea54ab3d011f91bf036b2
EDIT: sorry for my english, maybe i am notz describing it correctly, please, could you look on image?
Regedit
I am looking for string name 00036601 - marked in image. Thanks for help
EDIT2: i found way how to do it with cmd "REG QUERY HKCU\SOFTWARE\Microsoft /s /f 00036601"
But not with powershell...
You can search the registry with PowerShell. I do not have the same registry path as you have.
Get-ChildItem -Path HKCU:\Software\Microsoft\Office\16.0 `
-Recurse -ErrorAction SilentlyContinue |
Where-Object { $_.PSChildName -eq '00036601' }
If you must do it from a cmd.exe shell:
powershell -NoLogo -NoProfile ^
"Get-ChildItem -Path HKCU: -Recurse -ErrorAction SilentlyContinue | " ^
"Where-Object { $_.PSChildName -eq '00036601' }"
EDIT: that was never going to get there.
This works on my machine to find the IM enabled setting.
$r = Get-ChildItem -Path 'HKCU:/Software/Microsoft/Office/' -Recurse -ErrorAction SilentlyContinue |
Where-Object { $_.Property -eq 'EnablePresence' }
(Get-ItemProperty -Path $r.PSPath).EnablePresence
Please try this on your machine.
$r = Get-ChildItem -Path 'HKCU:/Software/Microsoft/Office/' -Recurse -ErrorAction SilentlyContinue |
Where-Object { $_.Property -eq '00036601' }
(Get-ItemProperty -Path $r.PSPath).'00036601'
I am trying to write a powershell script that does the following:
Check to see if a folder on a remote machine(text list of computers) exists, if so delete it.
Copy a folder from a remote share to the same machine and if there is an error output to an error log file, if not, output to a success log file.
I have searched but have been unable to find a solution to my seemingly simple problem, please see my code below:
$computers=Get-Content C:\pcs.txt
$source="\\RemoteShare\RemoteFolder"
$dest="C$\Program Files\Destination"
foreach ($computer in $computers) {
If (Test-Path \\$computer\$dest){
Remove-Item \\$computer\$dest -Force -Recurse
}
Copy-Item $source \\$computer\$dest -recurse -force -erroraction silentlycontinue
If (!$error)
{Write-Output $computer | out-file -append -filepath "C:\logs\success.log"}
Else
{Write-Output $computer | out-file -append -filepath "C:\logs\failed.log"}
}
Currently, when the script runs, everything is getting put in the failed.log file, regardless of if it fails or not.
How can I properly handle errors in powershell, while running through a for loop?
Here's an example.
$array = #(3,0,1,2)
foreach ($item in $array)
{
try
{
1/$item | Out-Null
$computer | Add-Content -Path "C:\logs\success.log"
}
catch
{
"Error: $_" | Add-Content -Path "C:\logs\failed.log"
}
}
Don't use $error, it always contains an array of recent error objects, even if the last command was successful. To check the results of the last command, use the $?, it will be false if the last command failed.
See about_Automatic_Variables for more details on these variables.