Compare commits
14 Commits
ffmpeg-msb
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
5486335855 | ||
|
e34c661509 | ||
|
6c20aa5cc9 | ||
|
bb50d8f1e9 | ||
|
fda2585b3c | ||
|
b79fc9f5ab | ||
|
f86828e569 | ||
|
0ba7aa56f8 | ||
|
27a2fc0d4f | ||
|
ba7833acb4 | ||
|
3a19ecb343 | ||
|
e4bfb708dc | ||
|
a1c0d07a71 | ||
|
c7cc8dc6ef |
37
.github/workflows/main.yml
vendored
37
.github/workflows/main.yml
vendored
@ -60,6 +60,8 @@ jobs:
|
|||||||
- win-x86
|
- win-x86
|
||||||
- win-x64
|
- win-x64
|
||||||
- linux-arm64
|
- linux-arm64
|
||||||
|
# Linux x86 is not supported by .NET
|
||||||
|
# - linux-x86
|
||||||
- linux-x64
|
- linux-x64
|
||||||
- osx-arm64
|
- osx-arm64
|
||||||
- osx-x64
|
- osx-x64
|
||||||
@ -71,10 +73,6 @@ jobs:
|
|||||||
artifact-name-base: YoutubeDownloader
|
artifact-name-base: YoutubeDownloader
|
||||||
- bundle-ffmpeg: false
|
- bundle-ffmpeg: false
|
||||||
artifact-name-base: YoutubeDownloader.Bare
|
artifact-name-base: YoutubeDownloader.Bare
|
||||||
exclude:
|
|
||||||
# FFmpeg builds for these platforms are not easily available
|
|
||||||
- bundle-ffmpeg: true
|
|
||||||
rid: linux-arm64
|
|
||||||
|
|
||||||
runs-on: ${{ startsWith(matrix.rid, 'win-') && 'windows-latest' || startsWith(matrix.rid, 'osx-') && 'macos-latest' || 'ubuntu-latest' }}
|
runs-on: ${{ startsWith(matrix.rid, 'win-') && 'windows-latest' || startsWith(matrix.rid, 'osx-') && 'macos-latest' || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
@ -92,22 +90,19 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
dotnet-version: 9.0.x
|
dotnet-version: 9.0.x
|
||||||
|
|
||||||
- name: Download FFmpeg
|
|
||||||
if: ${{ matrix.bundle-ffmpeg }}
|
|
||||||
shell: pwsh
|
|
||||||
run: YoutubeDownloader/DownloadFFmpeg.ps1 -platform ${{ matrix.rid }}
|
|
||||||
|
|
||||||
- name: Publish app
|
- name: Publish app
|
||||||
run: >
|
run: >
|
||||||
dotnet publish YoutubeDownloader
|
dotnet publish YoutubeDownloader
|
||||||
-p:Version=${{ github.ref_type == 'tag' && github.ref_name || format('999.9.9-ci-{0}', github.sha) }}
|
-p:Version=${{ github.ref_type == 'tag' && github.ref_name || format('999.9.9-ci-{0}', github.sha) }}
|
||||||
-p:CSharpier_Bypass=true
|
-p:CSharpier_Bypass=true
|
||||||
|
-p:DownloadFFmpeg=${{ matrix.bundle-ffmpeg }}
|
||||||
|
-p:PublishMacOSBundle=${{ startsWith(matrix.rid, 'osx-') }}
|
||||||
--output YoutubeDownloader/bin/publish
|
--output YoutubeDownloader/bin/publish
|
||||||
--configuration Release
|
--configuration Release
|
||||||
--runtime ${{ matrix.rid }}
|
--runtime ${{ matrix.rid }}
|
||||||
--self-contained
|
--self-contained
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload app binaries
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.artifact-name-base }}.${{ matrix.rid }}
|
name: ${{ matrix.artifact-name-base }}.${{ matrix.rid }}
|
||||||
@ -148,6 +143,8 @@ jobs:
|
|||||||
- win-x86
|
- win-x86
|
||||||
- win-x64
|
- win-x64
|
||||||
- linux-arm64
|
- linux-arm64
|
||||||
|
# Linux x86 is not supported by .NET
|
||||||
|
# - linux-x86
|
||||||
- linux-x64
|
- linux-x64
|
||||||
- osx-arm64
|
- osx-arm64
|
||||||
- osx-x64
|
- osx-x64
|
||||||
@ -159,10 +156,6 @@ jobs:
|
|||||||
artifact-name-base: YoutubeDownloader
|
artifact-name-base: YoutubeDownloader
|
||||||
- bundle-ffmpeg: false
|
- bundle-ffmpeg: false
|
||||||
artifact-name-base: YoutubeDownloader.Bare
|
artifact-name-base: YoutubeDownloader.Bare
|
||||||
exclude:
|
|
||||||
# FFmpeg builds for these platforms are not easily available
|
|
||||||
- bundle-ffmpeg: true
|
|
||||||
rid: linux-arm64
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
@ -172,19 +165,21 @@ jobs:
|
|||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Download artifacts
|
- name: Download app binaries
|
||||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.artifact-name-base }}.${{ matrix.rid }}
|
name: ${{ matrix.artifact-name-base }}.${{ matrix.rid }}
|
||||||
path: YoutubeDownloader/
|
path: YoutubeDownloader/
|
||||||
|
|
||||||
- name: Set permissions (app)
|
- name: Set permissions
|
||||||
if: ${{ !startsWith(matrix.rid, 'win-') }}
|
if: ${{ !startsWith(matrix.rid, 'win-') }}
|
||||||
run: chmod +x YoutubeDownloader/YoutubeDownloader
|
run: |
|
||||||
|
[ -f YoutubeDownloader/YoutubeDownloader ] && chmod +x YoutubeDownloader/YoutubeDownloader
|
||||||
- name: Set permissions (FFmpeg)
|
[ -f YoutubeDownloader/ffmpeg ] && chmod +x YoutubeDownloader/ffmpeg
|
||||||
if: ${{ !startsWith(matrix.rid, 'win-') && matrix.bundle-ffmpeg }}
|
|
||||||
run: chmod +x YoutubeDownloader/ffmpeg
|
# macOS bundle
|
||||||
|
[ -f YoutubeDownloader/YoutubeDownloader.app/Contents/MacOS/YoutubeDownloader] && chmod +x YoutubeDownloader/YoutubeDownloader.app/Contents/MacOS/YoutubeDownloader
|
||||||
|
[ -f YoutubeDownloader/YoutubeDownloader.app/Contents/MacOS/ffmpeg ] && chmod +x YoutubeDownloader/YoutubeDownloader.app/Contents/MacOS/ffmpeg
|
||||||
|
|
||||||
- name: Create package
|
- name: Create package
|
||||||
# Change into the artifacts directory to avoid including the directory itself in the zip archive
|
# Change into the artifacts directory to avoid including the directory itself in the zip archive
|
||||||
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -8,5 +8,8 @@
|
|||||||
bin/
|
bin/
|
||||||
obj/
|
obj/
|
||||||
|
|
||||||
|
# Avalonia
|
||||||
|
.avalonia-build-tasks/
|
||||||
|
|
||||||
# Test results
|
# Test results
|
||||||
TestResults/
|
TestResults/
|
||||||
|
@ -42,6 +42,10 @@ To learn more about the war and how you can help, [click here](https://tyrrrz.me
|
|||||||
- 🟢 **[Stable release](https://github.com/Tyrrrz/YoutubeDownloader/releases/latest)**
|
- 🟢 **[Stable release](https://github.com/Tyrrrz/YoutubeDownloader/releases/latest)**
|
||||||
- 🟠 [CI build](https://github.com/Tyrrrz/YoutubeDownloader/actions/workflows/main.yml)
|
- 🟠 [CI build](https://github.com/Tyrrrz/YoutubeDownloader/actions/workflows/main.yml)
|
||||||
|
|
||||||
|
> **Important**:
|
||||||
|
> To launch the app on MacOS, you need to first remove the downloaded file from quarantine.
|
||||||
|
> You can do that by running the following command in the terminal: `xattr -rd com.apple.quarantine YoutubeDownloader.app`.
|
||||||
|
|
||||||
> **Note**:
|
> **Note**:
|
||||||
> If you're unsure which build is right for your system, consult with [this page](https://useragent.cc) to determine your OS and CPU architecture.
|
> If you're unsure which build is right for your system, consult with [this page](https://useragent.cc) to determine your OS and CPU architecture.
|
||||||
|
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Gress;
|
|
||||||
using YoutubeDownloader.Core.Utils;
|
using YoutubeDownloader.Core.Utils;
|
||||||
using YoutubeExplode;
|
using YoutubeExplode;
|
||||||
using YoutubeExplode.Channels;
|
using YoutubeExplode.Channels;
|
||||||
@ -122,33 +120,4 @@ public class QueryResolver(IReadOnlyList<Cookie>? initialCookies = null)
|
|||||||
?? await TryResolveChannelAsync(query, cancellationToken)
|
?? await TryResolveChannelAsync(query, cancellationToken)
|
||||||
?? await ResolveSearchAsync(query, cancellationToken);
|
?? await ResolveSearchAsync(query, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<QueryResult> ResolveAsync(
|
|
||||||
IReadOnlyList<string> queries,
|
|
||||||
IProgress<Percentage>? progress = null,
|
|
||||||
CancellationToken cancellationToken = default
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (queries.Count == 1)
|
|
||||||
return await ResolveAsync(queries.Single(), cancellationToken);
|
|
||||||
|
|
||||||
var videos = new List<IVideo>();
|
|
||||||
var videoIds = new HashSet<VideoId>();
|
|
||||||
|
|
||||||
var completed = 0;
|
|
||||||
foreach (var query in queries)
|
|
||||||
{
|
|
||||||
var result = await ResolveAsync(query, cancellationToken);
|
|
||||||
|
|
||||||
foreach (var video in result.Videos)
|
|
||||||
{
|
|
||||||
if (videoIds.Add(video.Id))
|
|
||||||
videos.Add(video);
|
|
||||||
}
|
|
||||||
|
|
||||||
progress?.Report(Percentage.FromFraction(1.0 * ++completed / queries.Count));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new QueryResult(QueryResultKind.Aggregate, $"{queries.Count} queries", videos);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,28 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using YoutubeExplode.Videos;
|
using YoutubeExplode.Videos;
|
||||||
|
|
||||||
namespace YoutubeDownloader.Core.Resolving;
|
namespace YoutubeDownloader.Core.Resolving;
|
||||||
|
|
||||||
public record QueryResult(QueryResultKind Kind, string Title, IReadOnlyList<IVideo> Videos);
|
public record QueryResult(QueryResultKind Kind, string Title, IReadOnlyList<IVideo> Videos)
|
||||||
|
{
|
||||||
|
public static QueryResult Aggregate(IReadOnlyList<QueryResult> results)
|
||||||
|
{
|
||||||
|
if (!results.Any())
|
||||||
|
throw new ArgumentException("Cannot aggregate empty results.", nameof(results));
|
||||||
|
|
||||||
|
return new QueryResult(
|
||||||
|
// Single query -> inherit kind, multiple queries -> aggregate
|
||||||
|
results.Count == 1
|
||||||
|
? results.Single().Kind
|
||||||
|
: QueryResultKind.Aggregate,
|
||||||
|
// Single query -> inherit title, multiple queries -> aggregate
|
||||||
|
results.Count == 1
|
||||||
|
? results.Single().Title
|
||||||
|
: $"{results.Count} queries",
|
||||||
|
// Combine all videos, deduplicate by ID
|
||||||
|
results.SelectMany(q => q.Videos).DistinctBy(v => v.Id).ToArray()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CSharpier.MsBuild" Version="0.30.6" PrivateAssets="all" />
|
<PackageReference Include="CSharpier.MsBuild" Version="1.0.2" PrivateAssets="all" />
|
||||||
<PackageReference Include="Gress" Version="2.1.1" />
|
<PackageReference Include="Gress" Version="2.1.1" />
|
||||||
<PackageReference Include="JsonExtensions" Version="1.2.0" />
|
<PackageReference Include="JsonExtensions" Version="1.2.0" />
|
||||||
<PackageReference Include="TagLibSharp" Version="2.3.0" />
|
<PackageReference Include="TagLibSharp" Version="2.3.0" />
|
||||||
<PackageReference Include="YoutubeExplode" Version="6.5.4" />
|
<PackageReference Include="YoutubeExplode" Version="6.5.4" />
|
||||||
<PackageReference Include="YoutubeExplode.Converter" Version="6.5.4" />
|
<PackageReference Include="YoutubeExplode.Converter" Version="6.5.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
</Project>
|
|
||||||
|
@ -5,7 +5,7 @@ VisualStudioVersion = 17.7.33920.267
|
|||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YoutubeDownloader", "YoutubeDownloader\YoutubeDownloader.csproj", "{AF6D645E-DDDD-4034-B644-D5328CC893C1}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YoutubeDownloader", "YoutubeDownloader\YoutubeDownloader.csproj", "{AF6D645E-DDDD-4034-B644-D5328CC893C1}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{131C2561-E5A1-43E8-BF38-40E2E23DB0A4}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Misc", "Misc", "{131C2561-E5A1-43E8-BF38-40E2E23DB0A4}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
Directory.Build.props = Directory.Build.props
|
Directory.Build.props = Directory.Build.props
|
||||||
License.txt = License.txt
|
License.txt = License.txt
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
xmlns:materialControls="clr-namespace:Material.Styles.Controls;assembly=Material.Styles"
|
xmlns:materialControls="clr-namespace:Material.Styles.Controls;assembly=Material.Styles"
|
||||||
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
xmlns:materialStyles="clr-namespace:Material.Styles.Themes;assembly=Material.Styles"
|
xmlns:materialStyles="clr-namespace:Material.Styles.Themes;assembly=Material.Styles"
|
||||||
|
Name="YoutubeDownloader"
|
||||||
ActualThemeVariantChanged="Application_OnActualThemeVariantChanged">
|
ActualThemeVariantChanged="Application_OnActualThemeVariantChanged">
|
||||||
<Application.DataTemplates>
|
<Application.DataTemplates>
|
||||||
<framework:ViewManager />
|
<framework:ViewManager />
|
||||||
|
68
YoutubeDownloader/Download-FFmpeg.ps1
Normal file
68
YoutubeDownloader/Download-FFmpeg.ps1
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
param (
|
||||||
|
[Parameter(Mandatory=$false)]
|
||||||
|
[string]$Platform,
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$false)]
|
||||||
|
[string]$OutputPath
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
# If the platform is not specified, use the current OS/arch
|
||||||
|
if (-not $Platform) {
|
||||||
|
$arch = [Runtime.InteropServices.RuntimeInformation]::OSArchitecture
|
||||||
|
|
||||||
|
if ($isWindows) {
|
||||||
|
$Platform = "windows-$arch"
|
||||||
|
} elseif ($isLinux) {
|
||||||
|
$Platform = "linux-$arch"
|
||||||
|
} elseif ($isMacOS) {
|
||||||
|
$Platform = "osx-$arch"
|
||||||
|
} else {
|
||||||
|
throw "Unsupported platform"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Normalize platform identifier
|
||||||
|
$Platform = $Platform.ToLower().Replace("win-", "windows-")
|
||||||
|
$fileName = if ($Platform.Contains("windows-")) { "ffmpeg.exe" } else { "ffmpeg" }
|
||||||
|
|
||||||
|
# If the output path is not specified, use the current directory
|
||||||
|
if (-not $OutputPath) {
|
||||||
|
$OutputPath = "$PSScriptRoot/$fileName"
|
||||||
|
}
|
||||||
|
|
||||||
|
# If the output path is an existing directory, append the default file name for the platform
|
||||||
|
if (Test-Path $OutputPath -PathType Container) {
|
||||||
|
$OutputPath = Join-Path $OutputPath $fileName
|
||||||
|
}
|
||||||
|
|
||||||
|
# Delete the existing file if it exists
|
||||||
|
if (Test-Path $OutputPath) {
|
||||||
|
Remove-Item $OutputPath
|
||||||
|
}
|
||||||
|
|
||||||
|
# Download the archive
|
||||||
|
Write-Host "Downloading FFmpeg for $Platform..."
|
||||||
|
$http = New-Object System.Net.WebClient
|
||||||
|
try {
|
||||||
|
$http.DownloadFile("https://github.com/Tyrrrz/FFmpegBin/releases/download/7.1.1/ffmpeg-$Platform.zip", "$OutputPath.zip")
|
||||||
|
} finally {
|
||||||
|
$http.Dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
# Extract FFmpeg
|
||||||
|
Add-Type -Assembly System.IO.Compression.FileSystem
|
||||||
|
$zip = [IO.Compression.ZipFile]::OpenRead("$OutputPath.zip")
|
||||||
|
try {
|
||||||
|
[IO.Compression.ZipFileExtensions]::ExtractToFile($zip.GetEntry($fileName), $OutputPath)
|
||||||
|
} finally {
|
||||||
|
$zip.Dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Done downloading FFmpeg."
|
||||||
|
} finally {
|
||||||
|
# Clean up
|
||||||
|
Remove-Item "$OutputPath.zip" -Force
|
||||||
|
}
|
@ -1,61 +0,0 @@
|
|||||||
param (
|
|
||||||
[string]$platform,
|
|
||||||
[string]$outputPath
|
|
||||||
)
|
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
|
|
||||||
# If the platform is not specified, use the current OS/arch
|
|
||||||
if (-not $platform) {
|
|
||||||
$arch = [Runtime.InteropServices.RuntimeInformation]::OSArchitecture
|
|
||||||
|
|
||||||
if ($isWindows) {
|
|
||||||
$platform = "windows-$arch"
|
|
||||||
} elseif ($isLinux) {
|
|
||||||
$platform = "linux-$arch"
|
|
||||||
} elseif ($isMacOS) {
|
|
||||||
$platform = "osx-$arch"
|
|
||||||
} else {
|
|
||||||
throw "Unsupported platform"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Normalize platform identifier
|
|
||||||
$platform = $platform.ToLower().Replace("win-", "windows-")
|
|
||||||
|
|
||||||
# If the output path is not specified, use the current directory
|
|
||||||
if (-not $outputPath) {
|
|
||||||
$fileName = if ($platform.Contains("windows-")) { "ffmpeg.exe" } else { "ffmpeg" }
|
|
||||||
$outputPath = "$PSScriptRoot/$fileName"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Delete the existing file if it exists
|
|
||||||
if (Test-Path $outputPath) {
|
|
||||||
Remove-Item $outputPath
|
|
||||||
}
|
|
||||||
|
|
||||||
# Download the archive
|
|
||||||
Write-Host "Downloading FFmpeg for $platform..."
|
|
||||||
$http = New-Object System.Net.WebClient
|
|
||||||
try {
|
|
||||||
$http.DownloadFile("https://github.com/Tyrrrz/FFmpegBin/releases/download/7.0/ffmpeg-$platform.zip", "$outputPath.zip")
|
|
||||||
} finally {
|
|
||||||
$http.Dispose()
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
# Extract FFmpeg
|
|
||||||
Add-Type -Assembly System.IO.Compression.FileSystem
|
|
||||||
$zip = [IO.Compression.ZipFile]::OpenRead("$outputPath.zip")
|
|
||||||
try {
|
|
||||||
$fileName = If ($platform.Contains("windows-")) { "ffmpeg.exe" } Else { "ffmpeg" }
|
|
||||||
[IO.Compression.ZipFileExtensions]::ExtractToFile($zip.GetEntry($fileName), $outputPath)
|
|
||||||
} finally {
|
|
||||||
$zip.Dispose()
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Done downloading FFmpeg."
|
|
||||||
} finally {
|
|
||||||
# Clean up
|
|
||||||
Remove-Item "$outputPath.zip" -Force
|
|
||||||
}
|
|
@ -65,7 +65,7 @@ public class DialogManager : IDisposable
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return file?.Path.LocalPath;
|
return file?.TryGetLocalPath() ?? file?.Path.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string?> PromptDirectoryPathAsync(string defaultDirPath = "")
|
public async Task<string?> PromptDirectoryPathAsync(string defaultDirPath = "")
|
||||||
@ -74,19 +74,21 @@ public class DialogManager : IDisposable
|
|||||||
Application.Current?.ApplicationLifetime?.TryGetTopLevel()
|
Application.Current?.ApplicationLifetime?.TryGetTopLevel()
|
||||||
?? throw new ApplicationException("Could not find the top-level visual element.");
|
?? throw new ApplicationException("Could not find the top-level visual element.");
|
||||||
|
|
||||||
var startLocation = await topLevel.StorageProvider.TryGetFolderFromPathAsync(
|
var result = await topLevel.StorageProvider.OpenFolderPickerAsync(
|
||||||
defaultDirPath
|
|
||||||
);
|
|
||||||
|
|
||||||
var folderPickResult = await topLevel.StorageProvider.OpenFolderPickerAsync(
|
|
||||||
new FolderPickerOpenOptions
|
new FolderPickerOpenOptions
|
||||||
{
|
{
|
||||||
AllowMultiple = false,
|
AllowMultiple = false,
|
||||||
SuggestedStartLocation = startLocation,
|
SuggestedStartLocation = await topLevel.StorageProvider.TryGetFolderFromPathAsync(
|
||||||
|
defaultDirPath
|
||||||
|
),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return folderPickResult.FirstOrDefault()?.Path.LocalPath;
|
var directory = result.FirstOrDefault();
|
||||||
|
if (directory is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return directory.TryGetLocalPath() ?? directory.Path.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() => _dialogLock.Dispose();
|
public void Dispose() => _dialogLock.Dispose();
|
||||||
|
87
YoutubeDownloader/Publish-MacOSBundle.ps1
Normal file
87
YoutubeDownloader/Publish-MacOSBundle.ps1
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
param(
|
||||||
|
[Parameter(Mandatory=$true)]
|
||||||
|
[string]$PublishDirPath,
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$true)]
|
||||||
|
[string]$IconsFilePath,
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$true)]
|
||||||
|
[string]$FullVersion,
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$true)]
|
||||||
|
[string]$ShortVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
# Setup paths
|
||||||
|
$tempDirPath = Join-Path $PublishDirPath "../publish-macos-app-temp"
|
||||||
|
$bundleName = "YoutubeDownloader.app"
|
||||||
|
$bundleDirPath = Join-Path $tempDirPath $bundleName
|
||||||
|
$contentsDirPath = Join-Path $bundleDirPath "Contents"
|
||||||
|
$macosDirPath = Join-Path $contentsDirPath "MacOS"
|
||||||
|
$resourcesDirPath = Join-Path $contentsDirPath "Resources"
|
||||||
|
|
||||||
|
try {
|
||||||
|
# Initialize the bundle's directory structure
|
||||||
|
New-Item -Path $bundleDirPath -ItemType Directory -Force
|
||||||
|
New-Item -Path $contentsDirPath -ItemType Directory -Force
|
||||||
|
New-Item -Path $macosDirPath -ItemType Directory -Force
|
||||||
|
New-Item -Path $resourcesDirPath -ItemType Directory -Force
|
||||||
|
|
||||||
|
# Copy icons into the .app's Resources folder
|
||||||
|
Copy-Item -Path $IconsFilePath -Destination (Join-Path $resourcesDirPath "AppIcon.icns") -Force
|
||||||
|
|
||||||
|
# Generate the Info.plist metadata file with the app information
|
||||||
|
$plistContent = @"
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>YoutubeDownloader</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>YoutubeDownloader</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>YoutubeDownloader</string>
|
||||||
|
<key>NSHumanReadableCopyright</key>
|
||||||
|
<string>© Oleksii Holub</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>me.Tyrrrz.YoutubeDownloader</string>
|
||||||
|
<key>CFBundleSpokenName</key>
|
||||||
|
<string>YoutubeDownloader</string>
|
||||||
|
<key>CFBundleIconFile</key>
|
||||||
|
<string>AppIcon</string>
|
||||||
|
<key>CFBundleIconName</key>
|
||||||
|
<string>AppIcon</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$FullVersion</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>$ShortVersion</string>
|
||||||
|
<key>NSHighResolutionCapable</key>
|
||||||
|
<true />
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
"@
|
||||||
|
|
||||||
|
Set-Content -Path (Join-Path $contentsDirPath "Info.plist") -Value $plistContent
|
||||||
|
|
||||||
|
# Delete the previous bundle if it exists
|
||||||
|
if (Test-Path (Join-Path $PublishDirPath $bundleName)) {
|
||||||
|
Remove-Item -Path (Join-Path $PublishDirPath $bundleName) -Recurse -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
# Move all files from the publish directory into the MacOS directory
|
||||||
|
Get-ChildItem -Path $PublishDirPath | ForEach-Object {
|
||||||
|
Move-Item -Path $_.FullName -Destination $macosDirPath -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
# Move the final bundle into the publish directory for upload
|
||||||
|
Move-Item -Path $bundleDirPath -Destination $PublishDirPath -Force
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
# Clean up the temporary directory
|
||||||
|
Remove-Item -Path $tempDirPath -Recurse -Force
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -21,6 +22,7 @@ namespace YoutubeDownloader.ViewModels.Components;
|
|||||||
public partial class DashboardViewModel : ViewModelBase
|
public partial class DashboardViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
private readonly ViewModelManager _viewModelManager;
|
private readonly ViewModelManager _viewModelManager;
|
||||||
|
private readonly SnackbarManager _snackbarManager;
|
||||||
private readonly DialogManager _dialogManager;
|
private readonly DialogManager _dialogManager;
|
||||||
private readonly SettingsService _settingsService;
|
private readonly SettingsService _settingsService;
|
||||||
|
|
||||||
@ -28,24 +30,15 @@ public partial class DashboardViewModel : ViewModelBase
|
|||||||
private readonly ResizableSemaphore _downloadSemaphore = new();
|
private readonly ResizableSemaphore _downloadSemaphore = new();
|
||||||
private readonly AutoResetProgressMuxer _progressMuxer;
|
private readonly AutoResetProgressMuxer _progressMuxer;
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[NotifyPropertyChangedFor(nameof(IsProgressIndeterminate))]
|
|
||||||
[NotifyCanExecuteChangedFor(nameof(ProcessQueryCommand))]
|
|
||||||
[NotifyCanExecuteChangedFor(nameof(ShowAuthSetupCommand))]
|
|
||||||
[NotifyCanExecuteChangedFor(nameof(ShowSettingsCommand))]
|
|
||||||
public partial bool IsBusy { get; set; }
|
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
[NotifyCanExecuteChangedFor(nameof(ProcessQueryCommand))]
|
|
||||||
public partial string? Query { get; set; }
|
|
||||||
|
|
||||||
public DashboardViewModel(
|
public DashboardViewModel(
|
||||||
ViewModelManager viewModelManager,
|
ViewModelManager viewModelManager,
|
||||||
|
SnackbarManager snackbarManager,
|
||||||
DialogManager dialogManager,
|
DialogManager dialogManager,
|
||||||
SettingsService settingsService
|
SettingsService settingsService
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_viewModelManager = viewModelManager;
|
_viewModelManager = viewModelManager;
|
||||||
|
_snackbarManager = snackbarManager;
|
||||||
_dialogManager = dialogManager;
|
_dialogManager = dialogManager;
|
||||||
_settingsService = settingsService;
|
_settingsService = settingsService;
|
||||||
|
|
||||||
@ -67,12 +60,23 @@ public partial class DashboardViewModel : ViewModelBase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
[NotifyPropertyChangedFor(nameof(IsProgressIndeterminate))]
|
||||||
|
[NotifyCanExecuteChangedFor(nameof(ProcessQueryCommand))]
|
||||||
|
[NotifyCanExecuteChangedFor(nameof(ShowAuthSetupCommand))]
|
||||||
|
[NotifyCanExecuteChangedFor(nameof(ShowSettingsCommand))]
|
||||||
|
public partial bool IsBusy { get; set; }
|
||||||
|
|
||||||
public ProgressContainer<Percentage> Progress { get; } = new();
|
public ProgressContainer<Percentage> Progress { get; } = new();
|
||||||
|
|
||||||
public ObservableCollection<DownloadViewModel> Downloads { get; } = [];
|
|
||||||
|
|
||||||
public bool IsProgressIndeterminate => IsBusy && Progress.Current.Fraction is <= 0 or >= 1;
|
public bool IsProgressIndeterminate => IsBusy && Progress.Current.Fraction is <= 0 or >= 1;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
[NotifyCanExecuteChangedFor(nameof(ProcessQueryCommand))]
|
||||||
|
public partial string? Query { get; set; }
|
||||||
|
|
||||||
|
public ObservableCollection<DownloadViewModel> Downloads { get; } = [];
|
||||||
|
|
||||||
private bool CanShowAuthSetup() => !IsBusy;
|
private bool CanShowAuthSetup() => !IsBusy;
|
||||||
|
|
||||||
[RelayCommand(CanExecute = nameof(CanShowAuthSetup))]
|
[RelayCommand(CanExecute = nameof(CanShowAuthSetup))]
|
||||||
@ -179,18 +183,41 @@ public partial class DashboardViewModel : ViewModelBase
|
|||||||
var resolver = new QueryResolver(_settingsService.LastAuthCookies);
|
var resolver = new QueryResolver(_settingsService.LastAuthCookies);
|
||||||
var downloader = new VideoDownloader(_settingsService.LastAuthCookies);
|
var downloader = new VideoDownloader(_settingsService.LastAuthCookies);
|
||||||
|
|
||||||
var result = await resolver.ResolveAsync(
|
// Split queries by newlines
|
||||||
Query.Split(
|
var queries = Query.Split(
|
||||||
"\n",
|
'\n',
|
||||||
StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries
|
StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries
|
||||||
),
|
|
||||||
progress
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Single video
|
// Process individual queries
|
||||||
if (result.Videos.Count == 1)
|
var queryResults = new List<QueryResult>();
|
||||||
|
foreach (var (i, query) in queries.Index())
|
||||||
{
|
{
|
||||||
var video = result.Videos.Single();
|
try
|
||||||
|
{
|
||||||
|
queryResults.Add(await resolver.ResolveAsync(query));
|
||||||
|
}
|
||||||
|
// If it's not the only query in the list, don't interrupt the process
|
||||||
|
// and report the error via an async notification instead of a sync dialog.
|
||||||
|
// https://github.com/Tyrrrz/YoutubeDownloader/issues/563
|
||||||
|
catch (YoutubeExplodeException ex)
|
||||||
|
when (ex is VideoUnavailableException or PlaylistUnavailableException
|
||||||
|
&& queries.Length > 1
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_snackbarManager.Notify(ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
progress.Report(Percentage.FromFraction((i + 1.0) / queries.Length));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aggregate results
|
||||||
|
var queryResult = QueryResult.Aggregate(queryResults);
|
||||||
|
|
||||||
|
// Single video result
|
||||||
|
if (queryResult.Videos.Count == 1)
|
||||||
|
{
|
||||||
|
var video = queryResult.Videos.Single();
|
||||||
|
|
||||||
var downloadOptions = await downloader.GetDownloadOptionsAsync(
|
var downloadOptions = await downloader.GetDownloadOptionsAsync(
|
||||||
video.Id,
|
video.Id,
|
||||||
@ -209,14 +236,14 @@ public partial class DashboardViewModel : ViewModelBase
|
|||||||
Query = "";
|
Query = "";
|
||||||
}
|
}
|
||||||
// Multiple videos
|
// Multiple videos
|
||||||
else if (result.Videos.Count > 1)
|
else if (queryResult.Videos.Count > 1)
|
||||||
{
|
{
|
||||||
var downloads = await _dialogManager.ShowDialogAsync(
|
var downloads = await _dialogManager.ShowDialogAsync(
|
||||||
_viewModelManager.CreateDownloadMultipleSetupViewModel(
|
_viewModelManager.CreateDownloadMultipleSetupViewModel(
|
||||||
result.Title,
|
queryResult.Title,
|
||||||
result.Videos,
|
queryResult.Videos,
|
||||||
// Pre-select videos if they come from a single query and not from search
|
// Pre-select videos if they come from a single query and not from search
|
||||||
result.Kind
|
queryResult.Kind
|
||||||
is not QueryResultKind.Search
|
is not QueryResultKind.Search
|
||||||
and not QueryResultKind.Aggregate
|
and not QueryResultKind.Aggregate
|
||||||
)
|
)
|
||||||
|
@ -62,7 +62,9 @@ public partial class MainViewModel(
|
|||||||
$"""
|
$"""
|
||||||
You're using a development build of {Program.Name}. These builds are not thoroughly tested and may contain bugs.
|
You're using a development build of {Program.Name}. These builds are not thoroughly tested and may contain bugs.
|
||||||
|
|
||||||
Auto-updates are disabled for development builds. If you want to switch to a stable release, please download it manually.
|
Auto-updates are disabled for development builds.
|
||||||
|
|
||||||
|
Click SEE RELEASES if you want to download a stable release instead.
|
||||||
""",
|
""",
|
||||||
"SEE RELEASES",
|
"SEE RELEASES",
|
||||||
"CLOSE"
|
"CLOSE"
|
||||||
@ -82,7 +84,7 @@ public partial class MainViewModel(
|
|||||||
$"""
|
$"""
|
||||||
FFmpeg is required for {Program.Name} to work. Please download it and make it available in the application directory or on the system PATH.
|
FFmpeg is required for {Program.Name} to work. Please download it and make it available in the application directory or on the system PATH.
|
||||||
|
|
||||||
Alternatively, you can also download a version of {Program.Name} that has FFmpeg bundled with it.
|
Alternatively, you can also download a version of {Program.Name} that has FFmpeg bundled with it. Look for release assets that are NOT marked as *.Bare.
|
||||||
|
|
||||||
Click DOWNLOAD to go to the FFmpeg download page.
|
Click DOWNLOAD to go to the FFmpeg download page.
|
||||||
""",
|
""",
|
||||||
|
@ -137,7 +137,7 @@
|
|||||||
HorizontalScrollBarVisibility="Disabled"
|
HorizontalScrollBarVisibility="Disabled"
|
||||||
IsVisible="{Binding !!Downloads.Count}"
|
IsVisible="{Binding !!Downloads.Count}"
|
||||||
ItemsSource="{Binding Downloads}"
|
ItemsSource="{Binding Downloads}"
|
||||||
VerticalScrollBarVisibility="Auto">
|
VerticalScrollBarVisibility="Visible">
|
||||||
<DataGrid.ContextMenu>
|
<DataGrid.ContextMenu>
|
||||||
<ContextMenu>
|
<ContextMenu>
|
||||||
<MenuItem Command="{Binding RemoveSuccessfulDownloadsCommand}" Header="Remove successful downloads" />
|
<MenuItem Command="{Binding RemoveSuccessfulDownloadsCommand}" Header="Remove successful downloads" />
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
CloseOnClickAway="False"
|
CloseOnClickAway="False"
|
||||||
DisableOpeningAnimation="True"
|
DisableOpeningAnimation="True"
|
||||||
Loaded="DialogHost_OnLoaded">
|
Loaded="DialogHost_OnLoaded">
|
||||||
<materialStyles:SnackbarHost HostName="Root">
|
<materialStyles:SnackbarHost HostName="Root" SnackbarMaxCounts="3">
|
||||||
<ContentControl Content="{Binding Dashboard}" />
|
<ContentControl Content="{Binding Dashboard}" />
|
||||||
</materialStyles:SnackbarHost>
|
</materialStyles:SnackbarHost>
|
||||||
</dialogHostAvalonia:DialogHost>
|
</dialogHostAvalonia:DialogHost>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<ApplicationIcon>..\favicon.ico</ApplicationIcon>
|
<ApplicationIcon>..\favicon.ico</ApplicationIcon>
|
||||||
@ -11,25 +10,33 @@
|
|||||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||||
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<DownloadFFmpeg>true</DownloadFFmpeg>
|
||||||
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AvaloniaResource Include="..\favicon.ico" Link="favicon.ico" />
|
<AvaloniaResource Include="..\favicon.ico" Link="favicon.ico" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="ffmpeg.exe" CopyToOutputDirectory="PreserveNewest" Condition="Exists('ffmpeg.exe')" />
|
<None
|
||||||
|
Include="ffmpeg.exe"
|
||||||
|
CopyToOutputDirectory="PreserveNewest"
|
||||||
|
Condition="Exists('ffmpeg.exe')"
|
||||||
|
/>
|
||||||
<None Include="ffmpeg" CopyToOutputDirectory="PreserveNewest" Condition="Exists('ffmpeg')" />
|
<None Include="ffmpeg" CopyToOutputDirectory="PreserveNewest" Condition="Exists('ffmpeg')" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AsyncImageLoader.Avalonia" Version="3.3.0" />
|
<PackageReference Include="AsyncImageLoader.Avalonia" Version="3.3.0" />
|
||||||
<PackageReference Include="Avalonia" Version="11.2.6" />
|
<PackageReference Include="Avalonia" Version="11.3.0" />
|
||||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.2.6" />
|
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.3.0" />
|
||||||
<PackageReference Include="Avalonia.Desktop" Version="11.2.6" />
|
<PackageReference Include="Avalonia.Desktop" Version="11.3.0" />
|
||||||
<PackageReference Include="Avalonia.Diagnostics" Version="11.2.6" Condition="'$(Configuration)' == 'Debug'" />
|
<PackageReference
|
||||||
|
Include="Avalonia.Diagnostics"
|
||||||
|
Version="11.3.0"
|
||||||
|
Condition="'$(Configuration)' == 'Debug'"
|
||||||
|
/>
|
||||||
<PackageReference Include="Cogwheel" Version="2.1.0" />
|
<PackageReference Include="Cogwheel" Version="2.1.0" />
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||||
<PackageReference Include="CSharpier.MsBuild" Version="0.30.6" PrivateAssets="all" />
|
<PackageReference Include="CSharpier.MsBuild" Version="1.0.2" PrivateAssets="all" />
|
||||||
<PackageReference Include="Deorcify" Version="1.1.0" PrivateAssets="all" />
|
<PackageReference Include="Deorcify" Version="1.1.0" PrivateAssets="all" />
|
||||||
<PackageReference Include="DialogHost.Avalonia" Version="0.9.2" />
|
<PackageReference Include="DialogHost.Avalonia" Version="0.9.2" />
|
||||||
<PackageReference Include="Gress" Version="2.1.1" />
|
<PackageReference Include="Gress" Version="2.1.1" />
|
||||||
@ -41,11 +48,9 @@
|
|||||||
<PackageReference Include="WebView.Avalonia" Version="11.0.0.1" />
|
<PackageReference Include="WebView.Avalonia" Version="11.0.0.1" />
|
||||||
<PackageReference Include="WebView.Avalonia.Desktop" Version="11.0.0.1" />
|
<PackageReference Include="WebView.Avalonia.Desktop" Version="11.0.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\YoutubeDownloader.Core\YoutubeDownloader.Core.csproj" />
|
<ProjectReference Include="..\YoutubeDownloader.Core\YoutubeDownloader.Core.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Avalonia.WebView is completely incompatible with trimming -->
|
<!-- Avalonia.WebView is completely incompatible with trimming -->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<TrimmerRootAssembly Include="Avalonia.WebView" />
|
<TrimmerRootAssembly Include="Avalonia.WebView" />
|
||||||
@ -59,5 +64,26 @@
|
|||||||
<TrimmerRootAssembly Include="WebView.Avalonia" />
|
<TrimmerRootAssembly Include="WebView.Avalonia" />
|
||||||
<TrimmerRootAssembly Include="WebView.Core" />
|
<TrimmerRootAssembly Include="WebView.Core" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<Target
|
||||||
</Project>
|
Name="DownloadFFmpeg"
|
||||||
|
BeforeTargets="Restore;PreBuildEvent"
|
||||||
|
Condition="$(DownloadFFmpeg) AND !Exists('ffmpeg.exe') AND !Exists('ffmpeg')"
|
||||||
|
>
|
||||||
|
<Exec
|
||||||
|
Command="pwsh -ExecutionPolicy Bypass -File $(ProjectDir)/Download-FFmpeg.ps1 -Platform $(RuntimeIdentifier) -OutputPath $(ProjectDir)"
|
||||||
|
LogStandardErrorAsError="true"
|
||||||
|
Condition="'$(RuntimeIdentifier)' != ''"
|
||||||
|
/>
|
||||||
|
<Exec
|
||||||
|
Command="pwsh -ExecutionPolicy Bypass -File $(ProjectDir)/Download-FFmpeg.ps1 -OutputPath $(ProjectDir)"
|
||||||
|
LogStandardErrorAsError="true"
|
||||||
|
Condition="'$(RuntimeIdentifier)' == ''"
|
||||||
|
/>
|
||||||
|
</Target>
|
||||||
|
<Target Name="PublishMacOSBundle" AfterTargets="Publish" Condition="$(PublishMacOSBundle)">
|
||||||
|
<Exec
|
||||||
|
Command="pwsh -ExecutionPolicy Bypass -File $(ProjectDir)/Publish-MacOSBundle.ps1 -PublishDirPath $(PublishDir) -IconsFilePath $(ProjectDir)/../favicon.icns -FullVersion $(Version) -ShortVersion $(AssemblyVersion)"
|
||||||
|
LogStandardErrorAsError="true"
|
||||||
|
/>
|
||||||
|
</Target>
|
||||||
|
</Project>
|
||||||
|
BIN
favicon.icns
Normal file
BIN
favicon.icns
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user