Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite in net 8 and refactor #5

Merged
merged 10 commits into from
Jan 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Build and test
on:
pull_request:
branches:
- master
paths:
- 'src/**'
- '.github\workflows\build-and-test.yml'

workflow_dispatch:

env:
DOTNET_VERSION: '8.*'

jobs:
build-and-test:
name: build-and-test-${{matrix.os}}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, ubuntu-latest]

steps:
- uses: actions/checkout@v3
- name: Setup .NET Core
uses: actions/setup-dotnet@v3
with:
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: Setup FFmpeg
uses: FedericoCarboni/setup-ffmpeg@v2
id: setup-ffmpeg

- name: Install dependencies
run: dotnet restore "src/PsVideoResolutionCmdlet.sln"

- name: Build
run: dotnet build "src/PsVideoResolutionCmdlet.sln" --configuration Release --no-restore

- name: Test
run: dotnet test "src/PsVideoResolutionCmdlet.Tests/PsVideoResolutionCmdlet.Tests.csproj" --no-restore --verbosity normal
22 changes: 22 additions & 0 deletions .github/workflows/outdated.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Outdated package checks

# Run workflow on pull request to the main branch
on:
pull_request:
types: [opened, synchronize, reopened]
branches: [ master ]

workflow_dispatch:

env:
SOLUTION_PATH: 'src/PsVideoResolutionCmdlet.sln'

jobs:
outdated-packages-check:
runs-on: ubuntu-latest

steps:
- uses: trossr32/[email protected]
with:
use-dotnet-outdated: true
dotnet-solution-path: ${{ env.SOLUTION_PATH }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,4 @@ MigrationBackup/

# Ionide (cross platform F# VS Code tools) working folder
.ionide/
src/PsVideoResolutionCmdlet/C/
90 changes: 54 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,24 @@ A Powershell module that probes video files for their resolution and output resu
Available in the [Powershell Gallery](https://www.powershellgallery.com/packages/VideoResolution)

## Description
Uses ffmpeg (ffprobe) to interrogate video files and retrieve the resolution and file size.
Uses ffmpeg (ffprobe) to interrogate video files and retrieve the resolution and file size. ffmpeg must be installed.

> [!NOTE]
> ffmpeg must be installed for this module to work. It can be installed using [chocolatey](https://chocolatey.org/packages/ffmpeg) or [scoop](https://scoop.sh/) or downloaded from [ffmpeg.org](https://ffmpeg.org/download.html).

Can be run against:

* all files in an input directory supplied as an InputDirectory parameter or from a user prompt.
* a file using the File parameter. This can be a file name in the current directory, a relative path, a full path, or a file name used in conjunction with the InputDirectory parameter.
* a collection of files piped into the module (note: this expects correct paths and won't use the InputDirectory parameter).
* all files in an input directory supplied as an `-InputDirectory` parameter.
* a file using the `-File` parameter. This can be a file name in the current directory, a relative path, a full path, or a file name used in conjunction with the `-InputDirectory` parameter.
* a collection of files piped into the module (note: this expects correct paths and won't use the `-InputDirectory` parameter).

If there is more than one result the module outputs the results to the host in 2 ordered lists, firstly by resolution and secondly by file name.

Results can also be output to log files if the user selects an output folder for logging when prompted or supplies a LogDirectory parameter.
Results can also be output to log and json files if an `-OutputDirectory` parameter is supplied.
A text log file is created that is a duplicate of the results written to the host.
A json file is created with an array representation of the VideoFile class.
A json file is created with an array representation of the `VideoInfo` class.

Alternatively return results as json using the `-Json` parameter, or as an object using the `-PSObject` parameter.

## Installation

Expand All @@ -30,68 +35,81 @@ Install-Module VideoResolution

## Parameters

#### -InputDirectory (alias -I)
*Optional*. If supplied this is the path used to find video files to process. If not supplied, the user will be
prompted to browse for and select the path to use. If used in conjunction with the File parameter then this path
will be joined to the file provided.
#### `-InputDirectory`
*Optional*. If supplied this is the path used to find video files to process. If used in conjunction with the File
parameter then this path will be joined to the file provided.

#### -File (alias -F)
*Optional*. If supplied this file will be processed. Must be a file in the current directory, a relative path, a full
#### `-OutputDirectory`
*Optional*. If supplied this is the path used to write text and json data files.

#### `-File`
*Optional*. If supplied, this file will be processed. Must be a file in the current directory, a relative path, a full
path, or a filename used in conjunction with the InputDirectory parameter.

#### -Files
*Optional*. Accepted as piped input. If supplied all files in this string array will be processed. Each file must be
in the current directory, a relative path or a full path. Cannot be used in conjunction with the InputDirectory parameter.
#### `-Files`
*Optional*. Accepted as piped input. If supplied, all files in this string array will be processed. Each file must be
in the current directory, a relative path, or a full path. Will be ignored if used in conjunction with the
InputDirectory parameter.

#### -LogDirectory (alias -L)
*Optional*. If supplied this is the path used to write text and json log files. If not supplied, the user will be
prompted firstly whether log files should be created, and if so then prompted to browse for and select the path to use.
#### `-Recursive`
*Optional*. If supplied along with an input directory, all sub-directories will also be searched for video files.

#### -NoLogs (alias -NL)
*Optional*. If supplied no log files will be created. Overrides the LogDirectory parameter.
#### `-Json`
*Optional*. If supplied json will be returned instead of the standard output.

#### -Quiet (alias -Q)
*Optional*. Removes verbose host output.
#### `-PSObject`
*Optional*. If supplied a PsObject will be returned instead of the standard output.

## Examples

All files in directory request which will prompt the user to select an input directory, whether log files should be created, and a log file directory:
Process the supplied file using the current directory

```powershell
PS C:\> Get-VideoResolution
PS C:\Videos\> Get-VideoResolution -File "ExampleFile.mkv"
```

All files in the supplied input directory, writing logs to the supplied log file directory and no verbose host output:
All files in the supplied input directory, writing json and log files to the supplied output directory.

```powershell
PS C:\> Get-VideoResolution -InputDirectory "C:\Videos" -LogDirectory "C:\Videos\Logs" -Quiet
PS C:\> Get-VideoResolution -InputDirectory "C:\Videos" -OutputDirectory "C:\Videos\Logs"
```

Process the supplied file with no logging:
Process the supplied file using the supplied input directory

```powershell
PS C:\> Get-VideoResolution -File "C:\Videos\ExampleFile.mkv" -NoLogs
PS C:\Videos\> Get-VideoResolution -File "ExampleFile.mkv" -InputDirectory "C:\Videos"
```

Process the supplied file using the current directory and prompt for whether log files should be created and the log file directory:
Process the supplied file with path

```powershell
PS C:\Videos\> Get-VideoResolution -File "ExampleFile.mkv"
PS C:\Videos\> Get-VideoResolution -File "C:\Videos\ExampleFile.mkv"
```

Process the piped files array, writing logs to the supplied log file directory and no verbose host output:
Process the supplied file and return json

```powershell
PS C:\> "C:\Videos\ExampleFile1.mkv","C:\Videos\ExampleFile2.mkv" | Get-VideoResolution -LogDirectory "C:\Videos\Logs" -Quiet
PS C:\Videos\> Get-VideoResolution -File "C:\Videos\ExampleFile.mkv" -Json
```

## Notes
A check is made to see whether ffmpeg is installed in the environment PATH or in a C:\ffmpeg\bin directory.
If ffmpeg is not found in either location, the user will be prompted to download ffmpeg which will be saved in the
C:\ffmpeg\bin directory.
Process the supplied file and return a PSObject

```powershell
PS C:\Videos\> Get-VideoResolution -File "C:\Videos\ExampleFile.mkv" -PSObject
```

Process the piped files array, writing json and log files to the supplied output directory.

```powershell
PS C:\> "C:\Videos\ExampleFile1.mkv","C:\Videos\ExampleFile2.mkv" | Get-VideoResolution -OutputDirectory "C:\Videos\Logs"

## Contribute

Please raise an issue if you find a bug or want to request a new feature, or create a pull request to contribute.

<a href='https://ko-fi.com/K3K22CEIT' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://cdn.ko-fi.com/cdn/kofi4.png?v=2' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>

## Credit

- [ffmpeg](https://ffmpeg.org/)
- [Xabe.FFmpeg](https://ffmpeg.xabe.net/)
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
function Get-VideoResolution {
<#
.SYNOPSIS
Probe video files for their resolution and output results to host and optionally to log files
Probe video files using ffmpeg (ffprobe) for their resolution and output results to host and optionally to log files

.DESCRIPTION
Uses ffmpeg (ffprobe) to interrogate video files and retrieve the resolution and file size.
Expand Down
95 changes: 95 additions & 0 deletions src/PsVideoResolution.Core/Formatters/OutputFormatter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using Newtonsoft.Json;
using PsVideoResolution.Core.Models;

namespace PsVideoResolution.Core.Formatters;

public static class OutputFormatter
{
private static readonly string[] ResolutionHeader = ["", "Ordered by resolution:", "----------------------"];
private static readonly string[] NameHeader = ["", "Ordered by name:", "----------------"];
private static readonly string[] ResultsHeader = ["", "Resolution Size (Mb) File", "---------- --------- ----"];

/// <summary>
/// Writes output files to the output directory
/// </summary>
/// <param name="results"></param>
/// <param name="outputDirectory"></param>
public static void WriteOutputFiles(this List<VideoInfo> results, string outputDirectory)
{
// build base file name
var now = DateTime.Now;

var baseFileName = $"VideoResolution_{now:yyyyMMdd_HHmmss}";

// write output log file
using var sw = new StreamWriter(Path.Combine(outputDirectory, $"{baseFileName}.log"));

foreach (var header in ResolutionHeader)
{
sw.WriteLine(header);
}

foreach (var result in results.OrderBy(r => r.Width))
{
sw.Write(result.ResultLine());
}

foreach (var header in NameHeader)
{
sw.WriteLine(header);
}

foreach (var result in results.OrderBy(r => r.File))
{
sw.WriteLine(result.ResultLine());
}

// write output json file
using var jsw = new StreamWriter(Path.Combine(outputDirectory, $"{baseFileName}.json"));
using var jw = new JsonTextWriter(jsw);

var serializer = new JsonSerializer
{
Formatting = Formatting.Indented
};

serializer.Serialize(jw, results);
}

/// <summary>
/// Gets the output for the host
/// </summary>
/// <param name="results"></param>
/// <returns></returns>
public static List<string> GetHostOutput(this List<VideoInfo> results)
{
List<string> output = ["", "Finished! Here are the results:"];

if (results.Count == 1)
{
output.AddRange(ResultsHeader);

output.AddRange(results.Select(r => r.ResultLine()));

output.Add("");

return output;
}

output.AddRange(ResolutionHeader);

output.AddRange(ResultsHeader);

output.AddRange(results.OrderBy(r => r.Width).Select(r => r.ResultLine()));

output.AddRange(NameHeader);

output.AddRange(ResultsHeader);

output.AddRange(results.OrderBy(r => r.File).Select(r => r.ResultLine()));

output.Add("");

return output;
}
}
6 changes: 6 additions & 0 deletions src/PsVideoResolution.Core/Models/JsonOutput.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace PsVideoResolution.Core.Models;

public class JsonOutput
{
public List<VideoInfo> Files { get; set; }

Check warning on line 5 in src/PsVideoResolution.Core/Models/JsonOutput.cs

View workflow job for this annotation

GitHub Actions / build-and-test-windows-latest

Non-nullable property 'Files' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 5 in src/PsVideoResolution.Core/Models/JsonOutput.cs

View workflow job for this annotation

GitHub Actions / build-and-test-windows-latest

Non-nullable property 'Files' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 5 in src/PsVideoResolution.Core/Models/JsonOutput.cs

View workflow job for this annotation

GitHub Actions / build-and-test-ubuntu-latest

Non-nullable property 'Files' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 5 in src/PsVideoResolution.Core/Models/JsonOutput.cs

View workflow job for this annotation

GitHub Actions / build-and-test-ubuntu-latest

Non-nullable property 'Files' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
}
70 changes: 70 additions & 0 deletions src/PsVideoResolution.Core/Models/VideoInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using ByteSizeLib;
using Newtonsoft.Json;
using Xabe.FFmpeg;

namespace PsVideoResolution.Core.Models;

public class VideoInfo
{
public string File { get; set; }
public int? Width { get; set; }
public int? Height { get; set; }
public long SizeInBytes { get; set; }

public double SizeInMb => ByteSize.FromBytes(SizeInBytes).MegaBytes;
public double SizeInGb => ByteSize.FromBytes(SizeInBytes).GigaBytes;

public string Resolution() => $"{Width}x{Height}";

public string ResultLine()
{
var res = Resolution().PadRight(10);
var mb = $"{SizeInMb}Mb".PadRight(10);

return $"{res} {mb} {File}";
}

public VideoInfo() { }

Check warning on line 27 in src/PsVideoResolution.Core/Models/VideoInfo.cs

View workflow job for this annotation

GitHub Actions / build-and-test-windows-latest

Non-nullable property 'File' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 27 in src/PsVideoResolution.Core/Models/VideoInfo.cs

View workflow job for this annotation

GitHub Actions / build-and-test-windows-latest

Non-nullable property 'File' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 27 in src/PsVideoResolution.Core/Models/VideoInfo.cs

View workflow job for this annotation

GitHub Actions / build-and-test-ubuntu-latest

Non-nullable property 'File' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 27 in src/PsVideoResolution.Core/Models/VideoInfo.cs

View workflow job for this annotation

GitHub Actions / build-and-test-ubuntu-latest

Non-nullable property 'File' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

public VideoInfo(string json, string file)
{
var info = JsonConvert.DeserializeObject<VideoInfoJson>(json);

File = file;
Width = info.Streams[0].Width;

Check warning on line 34 in src/PsVideoResolution.Core/Models/VideoInfo.cs

View workflow job for this annotation

GitHub Actions / build-and-test-windows-latest

Dereference of a possibly null reference.

Check warning on line 34 in src/PsVideoResolution.Core/Models/VideoInfo.cs

View workflow job for this annotation

GitHub Actions / build-and-test-windows-latest

Dereference of a possibly null reference.

Check warning on line 34 in src/PsVideoResolution.Core/Models/VideoInfo.cs

View workflow job for this annotation

GitHub Actions / build-and-test-ubuntu-latest

Dereference of a possibly null reference.

Check warning on line 34 in src/PsVideoResolution.Core/Models/VideoInfo.cs

View workflow job for this annotation

GitHub Actions / build-and-test-ubuntu-latest

Dereference of a possibly null reference.
Height = info.Streams[0].Height;
SizeInBytes = info.Format.Size;
}

public VideoInfo(IMediaInfo info, string file)
{
File = file;
Width = info.VideoStreams.FirstOrDefault()?.Width;
Height = info.VideoStreams.FirstOrDefault()?.Height;
SizeInBytes = info.Size;
}

private class VideoInfoJson
{
[JsonProperty("format")]
public Format Format { get; set; }

Check warning on line 50 in src/PsVideoResolution.Core/Models/VideoInfo.cs

View workflow job for this annotation

GitHub Actions / build-and-test-windows-latest

Non-nullable property 'Format' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 50 in src/PsVideoResolution.Core/Models/VideoInfo.cs

View workflow job for this annotation

GitHub Actions / build-and-test-windows-latest

Non-nullable property 'Format' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 50 in src/PsVideoResolution.Core/Models/VideoInfo.cs

View workflow job for this annotation

GitHub Actions / build-and-test-ubuntu-latest

Non-nullable property 'Format' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 50 in src/PsVideoResolution.Core/Models/VideoInfo.cs

View workflow job for this annotation

GitHub Actions / build-and-test-ubuntu-latest

Non-nullable property 'Format' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

[JsonProperty("streams")]
public Stream[] Streams { get; set; }

Check warning on line 53 in src/PsVideoResolution.Core/Models/VideoInfo.cs

View workflow job for this annotation

GitHub Actions / build-and-test-windows-latest

Non-nullable property 'Streams' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 53 in src/PsVideoResolution.Core/Models/VideoInfo.cs

View workflow job for this annotation

GitHub Actions / build-and-test-windows-latest

Non-nullable property 'Streams' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 53 in src/PsVideoResolution.Core/Models/VideoInfo.cs

View workflow job for this annotation

GitHub Actions / build-and-test-ubuntu-latest

Non-nullable property 'Streams' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

Check warning on line 53 in src/PsVideoResolution.Core/Models/VideoInfo.cs

View workflow job for this annotation

GitHub Actions / build-and-test-ubuntu-latest

Non-nullable property 'Streams' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
}

private class Format
{
[JsonProperty("size")]
public int Size { get; set; }
}

private class Stream
{
[JsonProperty("width")]
public int Width { get; set; }

[JsonProperty("height")]
public int Height { get; set; }
}
}
Loading