Skip to content

Commit

Permalink
Merge pull request #1047 from oxygen-dioxide/publish
Browse files Browse the repository at this point in the history
Add a singer publish tool to pack a singer into a zip file
  • Loading branch information
stakira authored Feb 25, 2024
2 parents 3fd29d4 + 21baa1c commit 2bf1f21
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 0 deletions.
83 changes: 83 additions & 0 deletions OpenUtau.Core/Classic/VoicebankPublisher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using Ignore;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;

using OpenUtau.Core.Ustx;

namespace OpenUtau.Classic {
public class VoicebankPublisher {
private readonly Action<double, string> progress;
private readonly Ignore.Ignore? ignore;

public VoicebankPublisher(Action<double, string> progress, string? gitIgnore) {
this.progress = progress;
if(gitIgnore != null) {
ignore = new Ignore.Ignore();
ignore.Add(gitIgnore.Split("\n"));
}
}

private static void ModifyConfig(USinger singer, Action<VoicebankConfig> modify) {
var yamlFile = Path.Combine(singer.Location, "character.yaml");
VoicebankConfig? config = null;
if (File.Exists(yamlFile)) {
using (var stream = File.OpenRead(yamlFile)) {
config = VoicebankConfig.Load(stream);
}
}
if (config == null) {
config = new VoicebankConfig();
}
modify(config);
using (var stream = File.Open(yamlFile, FileMode.Create)) {
config.Save(stream);
}
}

private bool IsIgnored(string relativePath){
return ignore?.IsIgnored(relativePath.Replace('\\', '/')) ?? false;
}

private List<string> GetFilesToPack(string singerPath)
{
List<string> fileList = Directory.EnumerateFiles(singerPath, "*.*", SearchOption.AllDirectories).ToList();
List<string> packList = fileList.FindAll(x => !IsIgnored(System.IO.Path.GetRelativePath(singerPath, x)));
return packList;
}

///<summary>
///Compress a voicebank into an optimized zip archive for distribution.
///This function only supports voicebanks that follow the classic packaging model,
///including utau, enunu and diffsinger.
///Vogen voicebanks aren't supported.
///</summary>
public void Publish(USinger singer, string outputFile){
var location = singer.Location;
if(!Directory.Exists(location)){
return;
}
progress.Invoke(0, $"Publishing {singer.Name}");
//Write singer type into character.yaml
try {
ModifyConfig(singer, config => config.SingerType = singer.SingerType.ToString().ToLower());
} catch (Exception e) { }
var packList = GetFilesToPack(location);
int index = 0;
int fileCount = packList.Count();
using(ZipArchive archive = new ZipArchive(File.Create(outputFile), ZipArchiveMode.Create))
{
foreach (var absFilePath in packList)
{
index++;
progress.Invoke(100.0 * index / fileCount, $"Compressing {absFilePath}");
string reFilePath = Path.GetRelativePath(location, absFilePath);
archive.CreateEntryFromFile(absFilePath, reFilePath);
}
}
progress.Invoke(0, $"Published {singer.Name} to {outputFile}");
}
}
}
1 change: 1 addition & 0 deletions OpenUtau.Core/OpenUtau.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<ItemGroup>
<PackageReference Include="BunLabs.NAudio.Flac" Version="2.0.1" />
<PackageReference Include="Concentus.OggFile" Version="1.0.4" />
<PackageReference Include="Ignore" Version="0.1.50" />
<PackageReference Include="K4os.Hash.xxHash" Version="1.0.8" />
<PackageReference Include="Melanchall.DryWetMidi" Version="6.1.4" />
<PackageReference Include="NAudio.Core" Version="2.1.0" />
Expand Down
3 changes: 3 additions & 0 deletions OpenUtau.Core/Util/Preferences.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ public class SerializablePreferences {
public string PhoneticAssistant = string.Empty;
public string RecentOpenSingerDirectory = string.Empty;
public string RecentOpenProjectDirectory = string.Empty;

public bool VoicebankPublishUseIgnore = true;
public string VoicebankPublishIgnores = "#Adobe Audition\n*.pkf\n\n#UTAU Engines\n*.ctspec\n*.d4c\n*.dio\n*.frc\n*.frt\n*.frq\n*.harvest\n*.lessaudio\n*.llsm\n*.mrq\n*.pitchtier\n*.pkf\n*.platinum\n*.pmk\n*.star\n*.uspec\n*.vs4ufrq\n\n#UTAU related tools\n$read\n*.setParam-Scache\n*.lbp\n*.lbp.caches/*\n\n#OpenUtau\nerrors.txt";
}
}
}
3 changes: 3 additions & 0 deletions OpenUtau/FilePicker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ internal class FilePicker {
public static FilePickerFileType ArchiveFiles { get; } = new("Archive File") {
Patterns = new[] { "*.zip", "*.rar", "*.uar", "*.vogeon", "*.oudep" },
};
public static FilePickerFileType ZIP { get; } = new("ZIP") {
Patterns = new[] { "*.zip" },
};
public static FilePickerFileType EXE { get; } = new("EXE") {
Patterns = new[] { "*.exe" },
};
Expand Down
5 changes: 5 additions & 0 deletions OpenUtau/Strings/Strings.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,11 @@ Warning: this option removes custom presets.</system:String>
<system:String x:Key="singers.otoview.zoomin">Zoom In</system:String>
<system:String x:Key="singers.otoview.zoomout">Zoom Out</system:String>
<system:String x:Key="singers.playsample">Play sample</system:String>
<system:String x:Key="singers.publish">Publish Singer</system:String>
<system:String x:Key="singers.publish.description">Create an optimized zip package of your singer for distribution.</system:String>
<system:String x:Key="singers.publish.ignoretypes">Ignore these file types during packaging (gitignore syntax):</system:String>
<system:String x:Key="singers.publish.publish">Publish</system:String>
<system:String x:Key="singers.publish.useignore"> Use file type ignoring</system:String>
<system:String x:Key="singers.readme">Open readme.txt</system:String>
<system:String x:Key="singers.readme.notfound">readme.txt not found.</system:String>
<system:String x:Key="singers.refresh">Refresh</system:String>
Expand Down
46 changes: 46 additions & 0 deletions OpenUtau/ViewModels/SingerPublishViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.IO;
using System.Threading.Tasks;
using OpenUtau.Classic;
using OpenUtau.Core;
using OpenUtau.Core.Util;
using OpenUtau.Core.Ustx;
using ReactiveUI.Fody.Helpers;

namespace OpenUtau.App.ViewModels {
public class SingerPublishViewModel : ViewModelBase {
public USinger singer;
[Reactive] public bool UseIgnore { get; set; }
[Reactive] public string IgnoreTypes { get; set; }

public SingerPublishViewModel(USinger singer) {
this.singer = singer;
UseIgnore = Preferences.Default.VoicebankPublishUseIgnore;
IgnoreTypes = Preferences.Default.VoicebankPublishIgnores;
}

public Task Publish(string outputFile){
return Task.Run(() => {
try {
Preferences.Default.VoicebankPublishUseIgnore = UseIgnore;
if(UseIgnore){
Preferences.Default.VoicebankPublishIgnores = IgnoreTypes;
}
Preferences.Save();
if(Directory.Exists(singer.Location)){
var publisher = new VoicebankPublisher((progress, info) => {
DocManager.Inst.ExecuteCmd(new ProgressBarNotification(progress, info));
}, UseIgnore ? IgnoreTypes : null);
publisher.Publish(singer, outputFile);
}
else if(File.Exists(singer.Location)){
File.Copy(singer.Location, outputFile);
}
} finally {
new Task(() => {
DocManager.Inst.ExecuteCmd(new ProgressBarNotification(0, ""));
}).Start(DocManager.Inst.MainScheduler);
}
});
}
}
}
27 changes: 27 additions & 0 deletions OpenUtau/Views/SingerPublishDialog.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:OpenUtau.App.ViewModels"
mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="600"
x:Class="OpenUtau.App.Views.SingerPublishDialog"
Icon="/Assets/open-utau.ico"
WindowStartupLocation="CenterScreen"
MinWidth="500" MinHeight="500" Width="500" Height="500"
ExtendClientAreaToDecorationsHint="False"
Title="{DynamicResource singers.publish}">
<Grid Margin="{Binding $parent.WindowDecorationMargin}">
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Visible">
<StackPanel Margin="5">
<TextBlock Text="{DynamicResource singers.publish.description}"/>
<Grid ColumnDefinitions="Auto,Auto" RowDefinitions="25" VerticalAlignment="Center" Margin="4">
<CheckBox IsChecked="{Binding UseIgnore}" VerticalAlignment="Center" Content="Use file type ignoring"/>
<TextBlock Text="{DynamicResource singers.publish.useignore}" Grid.Column="1" VerticalAlignment="Center"/>
</Grid>
<TextBlock Text="{DynamicResource singers.publish.ignoretypes}"/>
<TextBox Text="{Binding IgnoreTypes}" Height="300" VerticalAlignment="Center" IsEnabled="{Binding UseIgnore}" AcceptsReturn="True"/>
<Button Content="{DynamicResource singers.publish.publish}" Click="PublishClicked" VerticalAlignment="Center" Margin="0,10,0,0"/>
</StackPanel>
</ScrollViewer>
</Grid>
</Window>
57 changes: 57 additions & 0 deletions OpenUtau/Views/SingerPublishDialog.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Platform.Storage;
using Serilog;
using OpenUtau.App.ViewModels;

namespace OpenUtau.App.Views {
public partial class SingerPublishDialog : Window {
public SingerPublishDialog() {
InitializeComponent();
}

async void PublishClicked(object sender, RoutedEventArgs arg){
var viewModel = DataContext as SingerPublishViewModel;
if (viewModel == null) {
return;
}
var singer = viewModel.singer;
if(singer == null){
return;
}
var types = FilePicker.ZIP;
if(File.Exists(singer.Location)){
var suffix = Path.GetExtension(singer.Location);
types = new FilePickerFileType(suffix.ToUpper()) {
Patterns = new[] { "*" + suffix },
};
}
var outputFile = await FilePicker.SaveFile(
this, "singers.publish", types);
if (outputFile == null) {
return;
}
Publish(outputFile);
}

void Publish(string outputFile){
var viewModel = DataContext as SingerPublishViewModel;
if (viewModel == null) {
return;
}
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
viewModel.Publish(outputFile).ContinueWith((task) => {
if (task.IsFaulted) {
Log.Error(task.Exception, "Failed to publish singer");
if (Parent is Window window) {
MessageBox.ShowError(window, task.Exception);
}
}
}, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, scheduler);
Close();
}
}
}
1 change: 1 addition & 0 deletions OpenUtau/Views/SingersDialog.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@
<CheckBox Classes="menu" IsChecked="{Binding UseFilenameAsAlias}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{DynamicResource singers.publish}" Click="OnPublish"/>
<MenuItem Header="{DynamicResource singers.errorreport}" Command="{Binding ErrorReport}"/>
<MenuItem Header="{DynamicResource singers.refresh}" Command="{Binding Refresh}"/>
</ContextMenu>
Expand Down
10 changes: 10 additions & 0 deletions OpenUtau/Views/SingersDialog.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,16 @@ async void OnSetPortrait(object sender, RoutedEventArgs args) {
}
}

async void OnPublish(object sender, RoutedEventArgs args) {
var viewModel = (DataContext as SingersViewModel)!;
if (viewModel.Singer == null) {
return;
}
var dialog = new SingerPublishDialog();
dialog.DataContext = new SingerPublishViewModel(viewModel.Singer);
await dialog.ShowDialog(this);
}

void OnSetUseFilenameAsAlias(object sender, RoutedEventArgs args) {
var viewModel = (DataContext as SingersViewModel)!;
viewModel.SetUseFilenameAsAlias();
Expand Down

0 comments on commit 2bf1f21

Please sign in to comment.