├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ └── dotnet.yml ├── .gitignore ├── Jenkinsfile.majorsilence ├── MediaPlayer.webp ├── MplayerTests.nunit ├── Readme.md ├── Release-Builds ├── build-release.ps1 ├── prepare-files-for-release.sh └── tools │ └── AssemblyInfoUtil.exe ├── build.sh ├── build_dockerimage.sh ├── deploy.sh ├── gpl-2.0.txt ├── lgpl-2.1.txt ├── majorsilence-media-web.service ├── majorsilence-media-workerservice.service └── src ├── .dockerignore ├── .idea ├── .gitignore ├── .idea.MPlayerControl │ └── .idea │ │ ├── .gitignore │ │ ├── indexLayout.xml │ │ ├── projectSettingsUpdater.xml │ │ └── vcs.xml ├── .idea.MPlayerGtkWidget │ └── .idea │ │ └── workspace.xml ├── .idea.Majorsilence.Avalonia.UI │ └── .idea │ │ ├── .gitignore │ │ ├── .name │ │ ├── avalonia.xml │ │ ├── encodings.xml │ │ ├── indexLayout.xml │ │ └── vcs.xml ├── .idea.Majorsilence.Media.Web │ └── .idea │ │ ├── .gitignore │ │ ├── .name │ │ ├── encodings.xml │ │ ├── indexLayout.xml │ │ └── vcs.xml └── .idea.Majorsilence.Winforms.MPlayerControl │ └── .idea │ ├── .gitignore │ ├── .name │ ├── encodings.xml │ ├── indexLayout.xml │ └── vcs.xml ├── Directory.Build.props ├── LibMPlayerWinform ├── LibMPlayerWinform.csproj ├── WinFormMPlayerControl.Designer.cs ├── WinFormMPlayerControl.cs └── WinFormMPlayerControl.resx ├── Majorsilence.Avalonia.UI.sln ├── Majorsilence.Media.Desktop.UI ├── .gitignore ├── App.axaml ├── App.axaml.cs ├── App.config ├── MainWindow.axaml ├── MainWindow.axaml.cs ├── Majorsilence.Media.Desktop.UI.csproj ├── PlayerProperties.axaml ├── PlayerProperties.axaml.cs ├── Program.cs ├── Properties │ ├── Settings.Designer.cs │ └── Settings.settings ├── VideoControl.cs └── app.manifest ├── Majorsilence.Media.Images ├── Filters.cs ├── ImageResize.cs └── Majorsilence.Media.Images.csproj ├── Majorsilence.Media.NunitTests ├── DiscoverTestCaseSource.cs ├── Discover_Test.cs ├── GlobalVariables.cs ├── ImageResize_Test.cs ├── MPlayer_Test.cs ├── Majorsilence.Media.NunitTests.csproj ├── Mencoder2_Test.cs ├── Properties │ └── AssemblyInfo.cs ├── SetupInitialize.cs ├── SlideShow_Test.cs ├── TestVideos │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ ├── 6.jpg │ ├── audio_license.txt │ ├── doxent_-_Sunset_Boulevard-edit.mp3 │ ├── video1.mp4 │ └── video2.mp4 ├── VideoEncoderTestCaseSource.cs └── VideoEncoder_Test.cs ├── Majorsilence.Media.Videos ├── AspectRatio.cs ├── AudioType.cs ├── BackendPrograms.cs ├── Discover.cs ├── DiscoverFactory.cs ├── Exceptions │ └── MPlayerControlException.cs ├── Ffmpeg.cs ├── FfmpegDiscover.cs ├── Globals.cs ├── IVideoEncoder.cs ├── Logging.cs ├── MPlayer.cs ├── MPlayerDiscover.cs ├── Majorsilence.Media.Videos.csproj ├── MediaStatus.cs ├── Mencoder.cs ├── MplayerBackends.cs ├── MplayerEvent.cs ├── Mpv.cs ├── MpvDiscover.cs ├── MpvPlayer.cs ├── PlatformCheck.cs ├── Player.cs ├── PlayerFactory.cs ├── RegionType.cs ├── Seek.cs ├── SlideShow.cs ├── TimeConversion.cs └── VideoType.cs ├── Majorsilence.Media.Web.sln ├── Majorsilence.Media.Web ├── Controllers │ └── ConvertController.cs ├── Dockerfile ├── JwtSecrets.cs ├── Majorsilence.Media.Web.csproj ├── Program.cs ├── Properties │ └── launchSettings.json ├── Settings.cs ├── appsettings.Development.json └── appsettings.json ├── Majorsilence.Media.WorkerService.Tests ├── GlobalUsings.cs ├── Majorsilence.Media.WorkerService.Tests.csproj └── TranscodingManagerTest.cs ├── Majorsilence.Media.WorkerService ├── Dockerfile ├── Majorsilence.Media.WorkerService.csproj ├── Program.cs ├── Properties │ └── launchSettings.json ├── Settings.cs ├── StreamTypes.cs ├── TranscodingManager.cs ├── Worker.cs ├── appsettings.Development.json └── appsettings.json ├── Majorsilence.Winforms.MPlayerControl.sln ├── MediaPlayer ├── MediaPlayer.csproj ├── Player.Designer.cs ├── Player.cs ├── Player.resx ├── PlayerProperties.Designer.cs ├── PlayerProperties.cs ├── PlayerProperties.resx ├── Program.cs ├── Properties │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── Resources │ ├── audio-volume-medium.png │ ├── audio-volume-muted.png │ ├── config.png │ ├── document-open.png │ ├── fastforward.png │ ├── pause.png │ ├── play.png │ ├── rewind.png │ ├── stop.png │ └── volume-down.png └── app.config ├── SlideShow ├── MainForm.Designer.cs ├── MainForm.cs ├── MainForm.resx ├── Program.cs ├── Properties │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── SlideShow.csproj └── app.config └── Test ├── Form1.Designer.cs ├── Form1.cs ├── Form1.resx ├── Program.cs ├── Properties ├── Resources.Designer.cs ├── Resources.resx ├── Settings.Designer.cs └── Settings.settings ├── Test.csproj └── Test2.cs /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description 4 | 5 | 6 | ## Motivation and Context 7 | 8 | 9 | 10 | ## How Has This Been Tested? 11 | 12 | 13 | 14 | 15 | ## Screenshots (if appropriate): 16 | 17 | ## Types of changes 18 | 19 | - [ ] Bug fix (non-breaking change which fixes an issue) 20 | - [ ] New feature (non-breaking change which adds functionality) 21 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 22 | 23 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "nuget" # See documentation for possible values 9 | directory: "/src" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: .NET 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | windows-build: 11 | runs-on: windows-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Setup .NET 15 | uses: actions/setup-dotnet@v4 16 | with: 17 | dotnet-version: 8.0.x 18 | - name: Build 19 | run: cd src && dotnet build Majorsilence.Winforms.MPlayerControl.sln -c Release 20 | 21 | linux-build: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v4 25 | - name: Setup .NET 26 | uses: actions/setup-dotnet@v4 27 | with: 28 | dotnet-version: 8.0.x 29 | - name: Build Avalonia 30 | run: cd src && dotnet build Majorsilence.Avalonia.UI.sln -c Release 31 | - name: Build Web 32 | run: cd src && dotnet build Majorsilence.Media.Web.sln -c Release 33 | - name: Install mplayer and mencoder and mpv 34 | run: sudo apt-get install -y mplayer mencoder mpv libmpv1 ffmpeg 35 | - name: Test 36 | run: cd src && dotnet test Majorsilence.Media.Web.sln --collect:"XPlat Code Coverage" --logger:"nunit" 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### VisualStudio ### 4 | ## Ignore Visual Studio temporary files, build results, and 5 | ## files generated by popular Visual Studio add-ons. 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Roslyn cache directories 26 | *.ide/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | #NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | *_i.c 42 | *_p.c 43 | *_i.h 44 | *.ilk 45 | *.meta 46 | *.obj 47 | *.pch 48 | *.pdb 49 | *.pgc 50 | *.pgd 51 | *.rsp 52 | *.sbr 53 | *.tlb 54 | *.tli 55 | *.tlh 56 | *.tmp 57 | *.tmp_proj 58 | *.log 59 | *.vspscc 60 | *.vssscc 61 | .builds 62 | *.pidb 63 | *.svclog 64 | *.scc 65 | 66 | # Chutzpah Test files 67 | _Chutzpah* 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | *.cachefile 76 | 77 | # Visual Studio profiler 78 | *.psess 79 | *.vsp 80 | *.vspx 81 | 82 | # TFS 2012 Local Workspace 83 | $tf/ 84 | 85 | # Guidance Automation Toolkit 86 | *.gpState 87 | 88 | # ReSharper is a .NET coding add-in 89 | _ReSharper*/ 90 | *.[Rr]e[Ss]harper 91 | *.DotSettings.user 92 | 93 | # JustCode is a .NET coding addin-in 94 | .JustCode 95 | 96 | # TeamCity is a build add-in 97 | _TeamCity* 98 | 99 | # DotCover is a Code Coverage Tool 100 | *.dotCover 101 | 102 | # NCrunch 103 | _NCrunch_* 104 | .*crunch*.local.xml 105 | 106 | # MightyMoose 107 | *.mm.* 108 | AutoTest.Net/ 109 | 110 | # Web workbench (sass) 111 | .sass-cache/ 112 | 113 | # Installshield output folder 114 | [Ee]xpress/ 115 | 116 | # DocProject is a documentation generator add-in 117 | DocProject/buildhelp/ 118 | DocProject/Help/*.HxT 119 | DocProject/Help/*.HxC 120 | DocProject/Help/*.hhc 121 | DocProject/Help/*.hhk 122 | DocProject/Help/*.hhp 123 | DocProject/Help/Html2 124 | DocProject/Help/html 125 | 126 | # Click-Once directory 127 | publish/ 128 | 129 | # Publish Web Output 130 | *.[Pp]ublish.xml 131 | *.azurePubxml 132 | # TODO: Comment the next line if you want to checkin your web deploy settings 133 | # but database connection strings (with potential passwords) will be unencrypted 134 | *.pubxml 135 | *.publishproj 136 | 137 | # NuGet Packages 138 | *.nupkg 139 | # The packages folder can be ignored because of Package Restore 140 | **/packages/* 141 | # except build/, which is used as an MSBuild target. 142 | !**/packages/build/ 143 | # If using the old MSBuild-Integrated Package Restore, uncomment this: 144 | #!**/packages/repositories.config 145 | 146 | # Windows Azure Build Output 147 | csx/ 148 | *.build.csdef 149 | 150 | # Windows Store app package directory 151 | AppPackages/ 152 | 153 | # Others 154 | sql/ 155 | *.Cache 156 | ClientBin/ 157 | [Ss]tyle[Cc]op.* 158 | ~$* 159 | *~ 160 | *.dbmdl 161 | *.dbproj.schemaview 162 | *.pfx 163 | *.publishsettings 164 | node_modules/ 165 | 166 | # RIA/Silverlight projects 167 | Generated_Code/ 168 | 169 | # Backup & report files from converting an old project file 170 | # to a newer Visual Studio version. Backup files are not needed, 171 | # because we have git ;-) 172 | _UpgradeReport_Files/ 173 | Backup*/ 174 | UpgradeLog*.XML 175 | UpgradeLog*.htm 176 | 177 | # SQL Server files 178 | *.mdf 179 | *.ldf 180 | 181 | # Business Intelligence projects 182 | *.rdl.data 183 | *.bim.layout 184 | *.bim_*.settings 185 | 186 | # Microsoft Fakes 187 | FakesAssemblies/ 188 | 189 | 190 | ### XamarinStudio ### 191 | bin/ 192 | obj/ 193 | *.userprefs 194 | 195 | Release-Builds/build-output 196 | Release-Builds/nuget/MPlayerControl/lib 197 | Release-Builds/nuget/MPlayerControl/content 198 | Release-Builds/nuget/MPlayerControl-Winform/lib 199 | Release-Builds/nuget/MPlayerControl-Gtk/lib 200 | Release-Builds/nunit-result.xml 201 | TestRun 202 | /.vs 203 | /src/.vs 204 | .DS_Store 205 | -------------------------------------------------------------------------------- /Jenkinsfile.majorsilence: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent none 3 | environment { 4 | DOTNET_CLI_HOME = '/tmp/DOTNET_CLI_HOME' 5 | DEPLOY_TARGET = credentials('majorsilence_media_host') // it will be something like example1.example.local 6 | } 7 | stages { 8 | stage('build and test') { 9 | agent { 10 | docker { 11 | image 'mcr.microsoft.com/dotnet/sdk:8.0' 12 | // args '-u root:sudo' 13 | } 14 | } 15 | steps { 16 | echo 'building' 17 | sh ''' 18 | #apt update && apt install -y mplayer mencoder mpv libmpv1 19 | ./build.sh 20 | ''' 21 | 22 | stash includes: 'build/linux-x64/**/*.tar.gz', name: 'linux-x64' 23 | //nunit testResultsPattern: '**/TestResults/*.xml' 24 | //recordCoverage(tools: [[parser: 'COBERTURA', pattern: '**/TestResults/**/*cobertura.xml']]) 25 | } 26 | } 27 | // Uncomment to build docker image 28 | //stage('build linux image') { 29 | // agent { label 'linux-x64' } 30 | // steps { 31 | // echo 'building' 32 | // sh ''' 33 | // ./build_dockerimage.sh 34 | // ''' 35 | // stash includes: "build/docker-images/**/*.tar.gz", name: 'docker-images' 36 | // } 37 | //} 38 | 39 | stage('Publish') { 40 | when { 41 | expression { return env.BRANCH_NAME.startsWith('PR-') == false } 42 | } 43 | steps { 44 | timeout(time:180, unit:'MINUTES') { 45 | input message:'Deploy?' 46 | node('linux-x64') { 47 | echo "Deploying linux-x64 media-web and media-worker systemd service ${env.BRANCH_NAME}...." 48 | unstash 'linux-x64' 49 | 50 | withCredentials([usernamePassword(credentialsId: 'media-web-ssh', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) { 51 | sh ''' 52 | ./deploy.sh remote_systemd_deploy "$USERNAME" "$PASSWORD" "$DEPLOY_TARGET" 53 | ''' 54 | } 55 | } 56 | } 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /MediaPlayer.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/MediaPlayer.webp -------------------------------------------------------------------------------- /MplayerTests.nunit: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Release-Builds/build-release.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | $ErrorActionPreference = "Stop" 3 | $CURRENTPATH = $pwd.Path 4 | 5 | 6 | function delete_files_and_folders([string]$path) { 7 | If (Test-Path $path) { 8 | Write-Host "Deleting path $path" -ForegroundColor Green 9 | Remove-Item -recurse -force $path 10 | } 11 | } 12 | 13 | delete_files_and_folders "$CURRENTPATH/build-output" 14 | 15 | 16 | # BUILD 17 | cd ../src 18 | dotnet restore Majorsilence.Winforms.MPlayerControl.sln 19 | dotnet build Majorsilence.Winforms.MPlayerControl.sln -p:Configuration="Release" 20 | 21 | 22 | # TESTS 23 | cd "$CURRENTPATH/../src/MplayerUnitTests/bin/Release/net6.0/" 24 | 25 | dotnet vstest "$CURRENTPATH/../src/MplayerUnitTests/bin/Release/net6.0/MplayerUnitTests.dll" --logger:"nunit;LogFileName=$CURRENTPATH/../src/MplayerUnitTests/bin/Release/net6.0/nunit-result.xml" 26 | 27 | cd "$CURRENTPATH" 28 | echo "tests finished" 29 | 30 | 31 | # OLD SCHOOL PACKAGE 32 | $PACKAGEDIR="MPlayerControl-dot-net-6.0" 33 | mkdir -p "./build-output/$PACKAGEDIR" 34 | 35 | Copy-Item ../src/Majorsilence.Media.Images/bin/Release/netstandard2.0/Majorsilence.Media.Images.dll -Destination "./build-output/$PACKAGEDIR/Majorsilence.Media.Images.dll" 36 | Copy-Item ../src/Majorsilence.Media.Videos/bin/Release/netstandard2.0/Majorsilence.Media.Videos.dll -Destination "./build-output/$PACKAGEDIR/Majorsilence.Media.Videos.dll" 37 | Copy-Item ../src/MediaPlayer/bin/Release/net6.0-windows/MediaPlayer.exe -Destination "./build-output/$PACKAGEDIR/MediaPlayer.exe" 38 | Copy-Item ../src/LibMPlayerWinform/bin/Release/net6.0-windows/LibMPlayerWinform.dll -Destination "./build-output/$PACKAGEDIR/LibMPlayerWinform.dll" 39 | Copy-Item ../src/SlideShow/bin/Release/net6.0-windows/SlideShow.exe -Destination "./build-output/$PACKAGEDIR/SlideShow.exe" 40 | 41 | 42 | cd build-output 43 | 7za a -t7z "$PACKAGEDIR.7z" -r $PACKAGEDIR -bd 44 | cd .. 45 | 46 | # NUGET 47 | 48 | cd "$CURRENTPATH/../src" 49 | Get-ChildItem -Recurse *.nupkg | Copy-Item -Destination "$CURRENTPATH\build-output" 50 | 51 | cd "$CURRENTPATH" 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /Release-Builds/prepare-files-for-release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e # exit on first error 3 | set -u # exit on using unset variable 4 | 5 | CURRENTPATH=`pwd` 6 | 7 | 8 | getos() 9 | { 10 | if [ "$(expr $(uname -s))" = "windows32" ]; then 11 | echo "windows" 12 | elif [ "$(expr substr $(uname -s) 1 5)" = "MINGW" ]; then 13 | echo "windows" 14 | else 15 | echo "linux" 16 | fi 17 | } 18 | 19 | read osversion junk <<< $(getos; echo $?) 20 | if [ "$osversion" = 'windows' ]; 21 | then 22 | # Increment minor number by 1 23 | # See http://www.codeproject.com/Articles/31236/How-To-Update-Assembly-Version-Number-Automaticall 24 | ./tools/AssemblyInfoUtil.exe -inc:2 "$CURRENTPATH/../LibImages/Properties/AssemblyInfo.cs" 25 | ./tools/AssemblyInfoUtil.exe -inc:2 "$CURRENTPATH/../LibMPlayerCommon/Properties/AssemblyInfo.cs" 26 | ./tools/AssemblyInfoUtil.exe -inc:2 "$CURRENTPATH/../MediaPlayer/Properties/AssemblyInfo.cs" 27 | ./tools/AssemblyInfoUtil.exe -inc:2 "$CURRENTPATH/../LibMPlayerWinform/Properties/AssemblyInfo.cs" 28 | ./tools/AssemblyInfoUtil.exe -inc:2 "$CURRENTPATH/../MPlayerGtkWidget/AssemblyInfo.cs" 29 | ./tools/AssemblyInfoUtil.exe -inc:2 "$CURRENTPATH/../SlideShow/Properties/AssemblyInfo.cs" 30 | ./tools/AssemblyInfoUtil.exe -inc:2 "$CURRENTPATH/../WpfMPlayer/Properties/AssemblyInfo.cs" 31 | else 32 | mono ./tools/AssemblyInfoUtil.exe -inc:2 "$CURRENTPATH/../LibImages/Properties/AssemblyInfo.cs" 33 | mono ./tools/AssemblyInfoUtil.exe -inc:2 "$CURRENTPATH/../LibMPlayerCommon/Properties/AssemblyInfo.cs" 34 | mono ./tools/AssemblyInfoUtil.exe -inc:2 "$CURRENTPATH/../MediaPlayer/Properties/AssemblyInfo.cs" 35 | mono ./tools/AssemblyInfoUtil.exe -inc:2 "$CURRENTPATH/../LibMPlayerWinform/Properties/AssemblyInfo.cs" 36 | mono ./tools/AssemblyInfoUtil.exe -inc:2 "$CURRENTPATH/../MPlayerGtkWidget/AssemblyInfo.cs" 37 | mono ./tools/AssemblyInfoUtil.exe -inc:2 "$CURRENTPATH/../SlideShow/Properties/AssemblyInfo.cs" 38 | mono ./tools/AssemblyInfoUtil.exe -inc:2 "$CURRENTPATH/../WpfMPlayer/Properties/AssemblyInfo.cs" 39 | fi 40 | 41 | VERSION_FULL_LINE=$(grep -F "AssemblyVersion" "$CURRENTPATH/../LibMPlayerCommon/Properties/AssemblyInfo.cs") 42 | VERSION_WITH_ASTERIK=$(echo $VERSION_FULL_LINE | awk -F\" '{print $(NF-1)}') 43 | VERSION="${VERSION_WITH_ASTERIK%??}" 44 | 45 | 46 | cd .. 47 | cd Release-Builds 48 | 49 | # Update NUGET spec files 50 | sed -i "5s/.*/ $VERSION<\/version>/" "$CURRENTPATH/nuget/MPlayerControl/MPlayerControl.nuspec" 51 | sed -i "5s/.*/ $VERSION<\/version>/" "$CURRENTPATH/nuget/MPlayerControl-Winform/MPlayerControl-Winform.nuspec" 52 | sed -i "21s/.*/ /" "$CURRENTPATH/nuget/MPlayerControl-Winform/MPlayerControl-Winform.nuspec" 53 | sed -i "5s/.*/ $VERSION<\/version>/" "$CURRENTPATH/nuget/MPlayerControl-Gtk/MPlayerControl-Gtk.nuspec" 54 | sed -i "21s/.*/ /" "$CURRENTPATH/nuget/MPlayerControl-Gtk/MPlayerControl-Gtk.nuspec" 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Release-Builds/tools/AssemblyInfoUtil.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/Release-Builds/tools/AssemblyInfoUtil.exe -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e # exit on first error 3 | set -u # exit on using unset variable 4 | 5 | #export LC_ALL=en_US.UTF-8 6 | CURRENTPATH=`pwd` 7 | VERSION=`cat src/Majorsilence.Media.Web/Majorsilence.Media.Web.csproj | grep "" | sed 's/[^0-9.]*//g'` 8 | 9 | clean_build(){ 10 | rm -rf ./build 11 | 12 | # Delete "bin" and "obj" folders 13 | rm -rf `find -type d -name bin` 14 | rm -rf `find -type d -name obj` 15 | rm -rf `find -type d -name TestResults` 16 | } 17 | 18 | 19 | remove_bom_from_file_encoding() 20 | { 21 | # hack to remove character encoding BOM if it exists 22 | STARTING_PATH=$1 23 | NAME_WILDCARD=$2 24 | # Find the first file matching the pattern 25 | file=$(find $STARTING_PATH -name "*cobertura.xml" | head -n 1) 26 | 27 | # Check if a file was found 28 | if [ -n "$file" ]; then 29 | # Remove the BOM and overwrite the original file 30 | /usr/bin/sed -i '1s/^\xEF\xBB\xBF//' "$file" 31 | echo "BOM removed from $file" 32 | else 33 | echo "No file matching the pattern was found." 34 | fi 35 | } 36 | 37 | systemd_package_build() 38 | { 39 | mkdir -p build/linux-x64 40 | dotnet restore src/Majorsilence.Media.Web.sln 41 | dotnet build -c Release src/Majorsilence.Media.Web.sln 42 | dotnet build "src/Majorsilence.Media.Web" -c Release -r linux-x64 43 | dotnet publish "src/Majorsilence.Media.Web" -c Release -r linux-x64 --self-contained true -o build/linux-x64/media-web-linux-x64-$VERSION 44 | #dotnet test src/Majorsilence.Media.Web.sln --collect:"XPlat Code Coverage" --logger:"nunit" 45 | #remove_bom_from_file_encoding "./src/Majorsilence.Media.Web/TestResults" "*cobertura.xml" 46 | dotnet build "src/Majorsilence.Media.WorkerService" -c Release -r linux-x64 47 | dotnet publish "src/Majorsilence.Media.WorkerService" -c Release -r linux-x64 --self-contained true -o build/linux-x64/media-workerservice-linux-x64-$VERSION 48 | 49 | cd build/linux-x64 50 | tar -czvf media-web-linux-x64-$VERSION.tar.gz media-web-linux-x64-$VERSION 51 | tar -czvf media-workerservice-linux-x64-$VERSION.tar.gz media-workerservice-linux-x64-$VERSION 52 | cd $CURRENTPATH 53 | } 54 | 55 | clean_build 56 | systemd_package_build 57 | -------------------------------------------------------------------------------- /build_dockerimage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e # exit on first error 3 | set -u # exit on using unset variable 4 | 5 | CURRENTPATH=`pwd` 6 | VERSION=`cat src/Majorsilence.Media.Web/Majorsilence.Media.Web.csproj | grep "" | sed 's/[^0-9.]*//g'` 7 | 8 | clean_build(){ 9 | rm -rf ./build/docker-images 10 | 11 | # Delete "bin" and "obj" folders 12 | rm -rf `find -type d -name bin` 13 | rm -rf `find -type d -name obj` 14 | } 15 | 16 | docker_build() 17 | { 18 | mkdir -p build/docker-images 19 | GITHASH="$(git rev-parse --short HEAD)" 20 | docker build -f ./src/Majorsilence.Media.Web/Majorsilence.Media.Web/Dockerfile -t majorsilence/media_web$GITHASH --rm=true . 21 | docker save majorsilence/media_web$GITHASH | gzip > ./build/docker-images/media_web.tar.gz 22 | docker rmi majorsilence/media_web$GITHASH 23 | 24 | docker build -f ./src/Majorsilence.Media.Web/Majorsilence.Media.Web/Dockerfile -t majorsilence/media_workerservice$GITHASH --rm=true . 25 | docker save majorsilence/media_workerservice$GITHASH | gzip > ./build/docker-images/media_workerservice.tar.gz 26 | docker rmi majorsilence/media_workerservice$GITHASH 27 | } 28 | 29 | clean_build 30 | docker_build 31 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e # exit on first error 3 | set -u # exit on using unset variable 4 | 5 | CURRENTPATH=$(pwd) 6 | VERSION=$(cat src/Majorsilence.Media.Web/Majorsilence.Media.Web.csproj | grep "" | sed 's/[^0-9.]*//g') 7 | 8 | echo "Deploying version $VERSION" 9 | 10 | USER=$2 11 | PASSWORD=$3 12 | SERVER=$4 13 | 14 | publish_server() { 15 | echo "deploy docker media_web" 16 | scp "$CURRENTPATH/build/media_web.tar.gz" $USER@$SERVER:/root 17 | 18 | ssh $USER@$SERVER '/snap/bin/docker stop media_web || true && /snap/bin/docker rm media_web || true \ 19 | && zcat /root/media_web.tar.gz | /snap/bin/docker load \ 20 | && /snap/bin/docker run -d -p 32001:8080 -v /root:/mediadata --restart always --name media_web majorsilence/media_web' 21 | 22 | echo "deploy docker media_workerservice" 23 | scp "$CURRENTPATH/build/media_workerservice.tar.gz" $USER@$SERVER:/root 24 | 25 | ssh $USER@$SERVER '/snap/bin/docker stop media_workerservice || true && /snap/bin/docker rm media_workerservice || true \ 26 | && zcat /root/media_workerservice.tar.gz | /snap/bin/docker load \ 27 | && /snap/bin/docker run -d -v /root:/mediadata --restart always --name media_workerservice majorsilence/media_workerservice' 28 | } 29 | 30 | remote_systemd_deploy() { 31 | echo "deploy systemd media-web" 32 | sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -l $USER $SERVER "cd /home/$USER && rm -rf ./media-web-*" 33 | 34 | sshpass -p "$PASSWORD" scp "$CURRENTPATH/build/linux-x64/media-web-linux-x64-$VERSION.tar.gz" $USER@$SERVER:/home/$USER 35 | 36 | sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -l $USER $SERVER "cd /home/$USER \ 37 | && tar xvf media-web-linux-x64-$VERSION.tar.gz \ 38 | && echo $PASSWORD | sudo -S rm -rf /opt/majorsilence/media-web/app \ 39 | && echo $PASSWORD | sudo -S mkdir -p /opt/majorsilence/media-web/app \ 40 | && echo $PASSWORD | sudo -S mkdir -p /opt/majorsilence/media-web/data/uploads \ 41 | && echo $PASSWORD | sudo -S cp --recursive /home/$USER/media-web-linux-x64-$VERSION/* /opt/majorsilence/media-web/app \ 42 | && rm -rf /home/$USER/media-web-* \ 43 | && echo $PASSWORD | sudo -S chown media-web:media-web -R /opt/majorsilence/media-web \ 44 | && echo $PASSWORD | sudo -S chmod +x /opt/majorsilence/media-web/app \ 45 | && echo $PASSWORD | sudo -S systemctl restart majorsilence-media-web" 46 | 47 | echo "deploy systemd media-workerservice" 48 | sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -l $USER $SERVER "cd /home/$USER && rm -rf ./media-workerservice-*" 49 | 50 | sshpass -p "$PASSWORD" scp "$CURRENTPATH/build/linux-x64/media-workerservice-linux-x64-$VERSION.tar.gz" $USER@$SERVER:/home/$USER 51 | 52 | sshpass -p "$PASSWORD" ssh -o StrictHostKeyChecking=no -l $USER $SERVER "cd /home/$USER \ 53 | && tar xvf media-workerservice-linux-x64-$VERSION.tar.gz \ 54 | && echo $PASSWORD | sudo -S rm -rf /opt/majorsilence/media-workerservice/app \ 55 | && echo $PASSWORD | sudo -S mkdir -p /opt/majorsilence/media-workerservice/app \ 56 | && echo $PASSWORD | sudo -S mkdir -p /opt/majorsilence/media-workerservice/data/converted \ 57 | && echo $PASSWORD | sudo -S cp --recursive /home/$USER/media-workerservice-linux-x64-$VERSION/* /opt/majorsilence/media-workerservice/app \ 58 | && rm -rf /home/$USER/media-workerservice-* \ 59 | && echo $PASSWORD | sudo -S chown media-workerservice:media-workerservice -R /opt/majorsilence/media-workerservice \ 60 | && echo $PASSWORD | sudo -S chmod +x /opt/majorsilence/media-workerservice/app \ 61 | && echo $PASSWORD | sudo -S systemctl restart majorsilence-media-workerservice" 62 | } 63 | 64 | if [ "$1" = "remote_docker_deploy" ]; then 65 | publish_server 66 | elif [ "$1" = "remote_systemd_deploy" ]; then 67 | remote_systemd_deploy 68 | elif [ "$1" = "local_systemd_deploy" ]; then 69 | local_systemd_deploy 70 | else 71 | echo "Only \"local_docker_install\", \"remote_docker_deploy\", \"remote_systemd_deploy\", or \"local_systemd_deploy\" supported." 72 | fi 73 | -------------------------------------------------------------------------------- /majorsilence-media-web.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Majorsilence Media Web API 3 | Wants=network-online.target 4 | After=network-online.target 5 | 6 | # How to install: 7 | # Copy /etc/systemd/system/majorsilence-media-web.service 8 | # systemctl daemon-reload 9 | # systemctl enable majorsilence-media-web.service 10 | # systemctl start majorsilence-media-web.service 11 | # Certificates 12 | # Production: use letsencrypt 13 | # Dev: self signed certs, change localhost to the host address 14 | # openssl req -x509 -newkey rsa:4096 -keyout /opt/majorsilence/media-web/certs/selfsigned.key -out /opt/majorsilence/media-web/certs/selfsigned.crt -days 365 -nodes -subj '/CN=localhost' 15 | # sudo -S chown media-web:media-web -R /opt/majorsilence/media-web 16 | 17 | [Service] 18 | # In production use a dedicated user/group. 19 | # sudo useradd -m media-web 20 | # To update permissions, use 'chown media-web:media-web -R /opt/majorsilence/media-web' to take ownership of the folder and files, 21 | # Use 'chmod +x /opt/majorsilence/media-web/app' to allow execution of the executable file. 22 | User=media-web 23 | 24 | ExecStart=/opt/majorsilence/media-web/app/Majorsilence.Media.Web 25 | Restart=always 26 | RestartSec=10 # Restart service after 10 seconds if node service crashes 27 | StandardOutput=syslog # Output to syslog" >> /etc/systemd/system/majorsilence-media-web.service 28 | StandardError=syslog # Output to syslog" >> /etc/systemd/system/majorsilence-media-web.service 29 | SyslogIdentifier=majorsilence-media-web 30 | WorkingDirectory=/opt/majorsilence/media-web/app 31 | 32 | # https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-8.0&tabs=linux-ubuntu 33 | KillSignal=SIGINT 34 | Environment=ASPNETCORE_ENVIRONMENT=Production 35 | Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false 36 | Environment=ASPNETCORE_URLS="http://+:8082" 37 | # if using certs comment out above line and uncomment the line below 38 | #Environment=ASPNETCORE_URLS="https://+:8083;http://+:8082" 39 | # if using a pfx uncomment the following two lines 40 | #Environment=ASPNETCORE_Kestrel__Certificates__Default__Password="testing_cert_password" 41 | #Environment=ASPNETCORE_Kestrel__Certificates__Default__Path="/opt/majorsilence/media-web/certs/majorsilence-media-web-certs.pfx" 42 | # if using crt and key uncomment following two lines 43 | #Environment=ASPNETCORE_Kestrel__Certificates__Default__Path="/opt/majorsilence/media-web/certs/selfsigned.crt" 44 | #Environment=ASPNETCORE_Kestrel__Certificates__Default__KeyPath="/opt/majorsilence/media-web/certs/selfsigned.key" 45 | Environment=ApiSettings__UploadFolder=/opt/majorsilence/media-web/data/uploads 46 | Environment=ApiSettings__PermittedCORS__0="https://*.majorsilence.com" 47 | Environment=ApiSettings__Jwt__Secret="PLACEHOLDER" 48 | Environment=ApiSettings__Jwt__Issuer="https://PLACEHOLDER" 49 | Environment=ApiSettings__Jwt__Audience="https://PLACEHOLDER" 50 | 51 | # https://www.freedesktop.org/software/systemd/man/systemd.exec.html 52 | NoNewPrivileges=yes 53 | PrivateTmp=yes 54 | ProtectSystem=full 55 | ProtectHome=yes 56 | 57 | [Install] 58 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /majorsilence-media-workerservice.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Majorsilence Media WorkerService API 3 | Wants=network-online.target 4 | After=network-online.target 5 | 6 | # How to install: 7 | # Copy /etc/systemd/system/majorsilence-media-workerservice.service 8 | # systemctl daemon-reload 9 | # systemctl enable majorsilence-media-workerservice.service 10 | # systemctl start majorsilence-media-workerservice.service 11 | 12 | 13 | [Service] 14 | # In production use a dedicated user/group. 15 | # sudo useradd -m media-workerservice 16 | # To update permissions, use 'chown media-workerservice:media-workerservice -R /opt/majorsilence/media-workerservice' to take ownership of the folder and files, 17 | # Use 'chmod +x /opt/majorsilence/media-workerservice/app' to allow execution of the executable file. 18 | User=media-workerservice 19 | 20 | ExecStart=/opt/majorsilence/media-workerservice/app/Majorsilence.Media.WorkerService 21 | Restart=always 22 | RestartSec=10 # Restart service after 10 seconds if node service crashes 23 | StandardOutput=syslog # Output to syslog" >> /etc/systemd/system/majorsilence-media-workerservice.service 24 | StandardError=syslog # Output to syslog" >> /etc/systemd/system/majorsilence-media-workerservice.service 25 | SyslogIdentifier=majorsilence-media-workerservice 26 | WorkingDirectory=/opt/majorsilence/media-workerservice/app 27 | 28 | # https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-8.0&tabs=linux-ubuntu 29 | KillSignal=SIGINT 30 | Environment=ASPNETCORE_ENVIRONMENT=Production 31 | Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false 32 | # UploadFolder should match the upload folder of Majorsilence.Media.Web 33 | # UploadFolder, give media-workerservice access in addition to media-web 34 | # sudo setfacl -R -m u:media-workerservice:rwx /opt/majorsilence/media-web/data/uploads 35 | # sudo setfacl -R -m d:u:media-workerservice:rwx /opt/majorsilence/media-web/data/uploads 36 | Environment=ApiSettings__UploadFolder=/opt/majorsilence/media-web/data/uploads 37 | Environment=ApiSettings__ConvertedFolder=/opt/majorsilence/media-workerservice/data/converted 38 | Environment=ApiSettings__FfmpegPath=/usr/bin/ffmpeg 39 | # Valid ConversionTypes are "streaming", "download", or "all" 40 | Environment=ApiSettings__ConversionType=streaming 41 | Environment=ApiSettings__StreamingTypes__MpegDash= 42 | Environment=ApiSettings__StreamingTypes__Hls=-i [PLACEHOLDER_INPUT] -map 0:v -map 0:a -s:v:0 426x240 -c:v:0 libx264 -b:v:0 250k -s:v:1 640x360 -c:v:1 libx264 -b:v:1 800k -s:v:2 854x480 -c:v:2 libx264 -b:v:2 1400k -s:v:3 1280x720 -c:v:3 libx264 -b:v:3 2800k -s:v:4 1920x1080 -c:v:4 libx264 -b:v:4 5000k -c:a aac -b:a 128k -hls_time 4 -hls_playlist_type vod -hls_segment_filename [PLACEHOLDER_OUTPUT]_%03d.ts [PLACEHOLDER_OUTPUT].m3u8 43 | 44 | # https://www.freedesktop.org/software/systemd/man/systemd.exec.html 45 | NoNewPrivileges=yes 46 | PrivateTmp=yes 47 | ProtectSystem=full 48 | ProtectHome=yes 49 | 50 | [Install] 51 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /src/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/azds.yaml 15 | **/bin 16 | **/charts 17 | **/docker-compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | LICENSE 25 | README.md -------------------------------------------------------------------------------- /src/.idea/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/src/.idea/.gitignore -------------------------------------------------------------------------------- /src/.idea/.idea.MPlayerControl/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /workspace.xml -------------------------------------------------------------------------------- /src/.idea/.idea.MPlayerControl/.idea/indexLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/.idea/.idea.MPlayerControl/.idea/projectSettingsUpdater.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /src/.idea/.idea.MPlayerControl/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/.idea/.idea.Majorsilence.Avalonia.UI/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Rider ignored files 5 | /projectSettingsUpdater.xml 6 | /modules.xml 7 | /.idea.MPlayerGtkWidget.iml 8 | /contentModel.xml 9 | # Editor-based HTTP Client requests 10 | /httpRequests/ 11 | # Datasource local storage ignored files 12 | /dataSources/ 13 | /dataSources.local.xml 14 | -------------------------------------------------------------------------------- /src/.idea/.idea.Majorsilence.Avalonia.UI/.idea/.name: -------------------------------------------------------------------------------- 1 | Majorsilence.Avalonia.UI -------------------------------------------------------------------------------- /src/.idea/.idea.Majorsilence.Avalonia.UI/.idea/avalonia.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | -------------------------------------------------------------------------------- /src/.idea/.idea.Majorsilence.Avalonia.UI/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/.idea/.idea.Majorsilence.Avalonia.UI/.idea/indexLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/.idea/.idea.Majorsilence.Avalonia.UI/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/.idea/.idea.Majorsilence.Media.Web/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Rider ignored files 5 | /projectSettingsUpdater.xml 6 | /contentModel.xml 7 | /.idea.Majorsilence.Media.Web.iml 8 | /modules.xml 9 | # Editor-based HTTP Client requests 10 | /httpRequests/ 11 | # Datasource local storage ignored files 12 | /dataSources/ 13 | /dataSources.local.xml 14 | # GitHub Copilot persisted chat sessions 15 | /copilot/chatSessions 16 | -------------------------------------------------------------------------------- /src/.idea/.idea.Majorsilence.Media.Web/.idea/.name: -------------------------------------------------------------------------------- 1 | Majorsilence.Media.Web -------------------------------------------------------------------------------- /src/.idea/.idea.Majorsilence.Media.Web/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/.idea/.idea.Majorsilence.Media.Web/.idea/indexLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/.idea/.idea.Majorsilence.Media.Web/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/.idea/.idea.Majorsilence.Winforms.MPlayerControl/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Rider ignored files 5 | /contentModel.xml 6 | /.idea.Majorsilence.Winforms.MPlayerControl.iml 7 | /projectSettingsUpdater.xml 8 | /modules.xml 9 | # Editor-based HTTP Client requests 10 | /httpRequests/ 11 | # Datasource local storage ignored files 12 | /dataSources/ 13 | /dataSources.local.xml 14 | # GitHub Copilot persisted chat sessions 15 | /copilot/chatSessions 16 | -------------------------------------------------------------------------------- /src/.idea/.idea.Majorsilence.Winforms.MPlayerControl/.idea/.name: -------------------------------------------------------------------------------- 1 | Majorsilence.Winforms.MPlayerControl -------------------------------------------------------------------------------- /src/.idea/.idea.Majorsilence.Winforms.MPlayerControl/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/.idea/.idea.Majorsilence.Winforms.MPlayerControl/.idea/indexLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/.idea/.idea.Majorsilence.Winforms.MPlayerControl/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | true 5 | https://github.com/majorsilence/MPlayerControl 6 | 1.9.0 7 | 1.9.0 8 | 1.9.0 9 | true 10 | Majorsilence Media 11 | Copyright © 2025 majorsilence (Peter Gill) and other contributors 12 | latest 13 | Media player control and server for dotnet. Winforms, avalonia desktop controls for playback via mplayer or mpv. 14 | Server side process video files using ffmpeg. 15 | 16 | -------------------------------------------------------------------------------- /src/LibMPlayerWinform/LibMPlayerWinform.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net6.0-windows;net8.0-windows;net9.0-windows 4 | enable 5 | true 6 | enable 7 | LibMPlayerWinform 8 | LibMPlayerWinform 9 | True 10 | 11 | 12 | full 13 | bin\$(Configuration)\ 14 | 1591 15 | bin\$(Configuration)\LibMPlayerWinform.xml 16 | 17 | 18 | bin\$(Configuration)\ 19 | 1591 20 | bin\$(Configuration)\LibMPlayerWinform.xml 21 | 22 | 23 | 24 | 25 | WinFormMPlayerControl.cs 26 | 27 | 28 | 29 | 30 | WinFormMPlayerControl.cs 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/LibMPlayerWinform/WinFormMPlayerControl.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace LibMPlayerWinform 2 | { 3 | partial class WinFormMPlayerControl 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Component Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.panelVideo = new System.Windows.Forms.Panel(); 32 | this.buttonPlay = new System.Windows.Forms.Button(); 33 | this.buttonStop = new System.Windows.Forms.Button(); 34 | this.SuspendLayout(); 35 | // 36 | // panelVideo 37 | // 38 | this.panelVideo.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 39 | | System.Windows.Forms.AnchorStyles.Left) 40 | | System.Windows.Forms.AnchorStyles.Right))); 41 | this.panelVideo.Location = new System.Drawing.Point(0, 0); 42 | this.panelVideo.Name = "panelVideo"; 43 | this.panelVideo.Size = new System.Drawing.Size(537, 222); 44 | this.panelVideo.TabIndex = 0; 45 | // 46 | // buttonPlay 47 | // 48 | this.buttonPlay.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 49 | this.buttonPlay.Location = new System.Drawing.Point(462, 228); 50 | this.buttonPlay.Name = "buttonPlay"; 51 | this.buttonPlay.Size = new System.Drawing.Size(75, 23); 52 | this.buttonPlay.TabIndex = 1; 53 | this.buttonPlay.Text = "Play"; 54 | this.buttonPlay.UseVisualStyleBackColor = true; 55 | this.buttonPlay.Click += new System.EventHandler(this.buttonPlay_Click); 56 | // 57 | // buttonStop 58 | // 59 | this.buttonStop.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 60 | this.buttonStop.Location = new System.Drawing.Point(367, 228); 61 | this.buttonStop.Name = "buttonStop"; 62 | this.buttonStop.Size = new System.Drawing.Size(75, 23); 63 | this.buttonStop.TabIndex = 2; 64 | this.buttonStop.Text = "Stop"; 65 | this.buttonStop.UseVisualStyleBackColor = true; 66 | this.buttonStop.Click += new System.EventHandler(this.buttonStop_Click); 67 | // 68 | // WinFormMPlayerControl 69 | // 70 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 71 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 72 | this.Controls.Add(this.buttonStop); 73 | this.Controls.Add(this.buttonPlay); 74 | this.Controls.Add(this.panelVideo); 75 | this.Name = "WinFormMPlayerControl"; 76 | this.Size = new System.Drawing.Size(540, 255); 77 | this.SizeChanged += new System.EventHandler(this.WinFormMPlayerControl_SizeChanged); 78 | this.ResumeLayout(false); 79 | 80 | } 81 | 82 | #endregion 83 | 84 | private System.Windows.Forms.Panel panelVideo; 85 | private System.Windows.Forms.Button buttonPlay; 86 | private System.Windows.Forms.Button buttonStop; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/LibMPlayerWinform/WinFormMPlayerControl.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Drawing; 5 | using System.Data; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Windows.Forms; 10 | using Majorsilence.Media.Videos; 11 | 12 | namespace LibMPlayerWinform 13 | { 14 | public partial class WinFormMPlayerControl : UserControl 15 | { 16 | Player _play; 17 | 18 | public WinFormMPlayerControl() 19 | { 20 | InitializeComponent(); 21 | } 22 | 23 | public WinFormMPlayerControl(Player play) 24 | { 25 | InitializeComponent(); 26 | 27 | _play = play; 28 | } 29 | 30 | public void SetPlayer(Player play) 31 | { 32 | _play = play; 33 | } 34 | 35 | public long Handle 36 | { 37 | get 38 | { 39 | return this.panelVideo.Handle.ToInt64(); 40 | } 41 | } 42 | 43 | private void buttonStop_Click(object sender, EventArgs e) 44 | { 45 | if (_play != null) 46 | { 47 | _play.Stop(); 48 | } 49 | } 50 | 51 | private void buttonPlay_Click(object sender, EventArgs e) 52 | { 53 | if (_play != null) 54 | { 55 | _play.Stop(); 56 | } 57 | 58 | if (_play == null) 59 | { 60 | throw new InvalidDataException("The player is not set. You must set it with the constructor or through the SetPlayer method."); 61 | } 62 | else if (System.IO.File.Exists(MPlayerPath) == false && _play is Majorsilence.Media.Videos.MPlayer) 63 | { 64 | throw new System.IO.FileNotFoundException("File not found", MPlayerPath); 65 | } 66 | else if (System.IO.File.Exists(MPlayerPath) == false && _play is Majorsilence.Media.Videos.MpvPlayer) 67 | { 68 | throw new System.IO.FileNotFoundException("File not found", MPlayerPath); 69 | } 70 | 71 | 72 | if (System.IO.File.Exists(VideoPath) == false && VideoPath.StartsWith("http") == false) 73 | { 74 | throw new System.IO.FileNotFoundException("File not found", VideoPath); 75 | } 76 | 77 | _play.Play(VideoPath); 78 | } 79 | 80 | [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] 81 | public string MPlayerPath { get; set; } 82 | 83 | [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] 84 | public string VideoPath { get; set; } 85 | 86 | private void WinFormMPlayerControl_SizeChanged(object sender, EventArgs e) 87 | { 88 | 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Majorsilence.Avalonia.UI.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.2.32630.192 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Majorsilence.Media.Images", "Majorsilence.Media.Images\Majorsilence.Media.Images.csproj", "{C859FA27-7882-4814-9202-9C9D807C2CE5}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Majorsilence.Media.Videos", "Majorsilence.Media.Videos\Majorsilence.Media.Videos.csproj", "{5D3616D0-160D-4601-AB67-E1ED9111C676}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Majorsilence.Media.Desktop.UI", "Majorsilence.Media.Desktop.UI\Majorsilence.Media.Desktop.UI.csproj", "{39B2C523-7644-491A-9D46-4D4C948150B4}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Majorsilence.Media.NunitTests", "Majorsilence.Media.NunitTests\Majorsilence.Media.NunitTests.csproj", "{5E050A99-68E9-4F34-82BD-4E04EAF64CA7}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {C859FA27-7882-4814-9202-9C9D807C2CE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {C859FA27-7882-4814-9202-9C9D807C2CE5}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {C859FA27-7882-4814-9202-9C9D807C2CE5}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {C859FA27-7882-4814-9202-9C9D807C2CE5}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {5D3616D0-160D-4601-AB67-E1ED9111C676}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {5D3616D0-160D-4601-AB67-E1ED9111C676}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {5D3616D0-160D-4601-AB67-E1ED9111C676}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {5D3616D0-160D-4601-AB67-E1ED9111C676}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {39B2C523-7644-491A-9D46-4D4C948150B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {39B2C523-7644-491A-9D46-4D4C948150B4}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {39B2C523-7644-491A-9D46-4D4C948150B4}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {39B2C523-7644-491A-9D46-4D4C948150B4}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {5E050A99-68E9-4F34-82BD-4E04EAF64CA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {5E050A99-68E9-4F34-82BD-4E04EAF64CA7}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {5E050A99-68E9-4F34-82BD-4E04EAF64CA7}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {5E050A99-68E9-4F34-82BD-4E04EAF64CA7}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {5238B2E8-5295-47DF-B781-F6760E906214} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /src/Majorsilence.Media.Desktop.UI/App.axaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Majorsilence.Media.Desktop.UI/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls.ApplicationLifetimes; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace Majorsilence.Media.Desktop.UI 6 | { 7 | public partial class App : Application 8 | { 9 | public override void Initialize() 10 | { 11 | AvaloniaXamlLoader.Load(this); 12 | } 13 | 14 | public override void OnFrameworkInitializationCompleted() 15 | { 16 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) 17 | { 18 | desktop.MainWindow = new MainWindow(); 19 | } 20 | 21 | base.OnFrameworkInitializationCompleted(); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Desktop.UI/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Majorsilence.Media.Desktop.UI/MainWindow.axaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Majorsilence.Media.Desktop.UI/MainWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using Avalonia.Controls; 4 | using Avalonia.Interactivity; 5 | using Majorsilence.Media.Videos; 6 | 7 | namespace Majorsilence.Media.Desktop.UI 8 | { 9 | public partial class MainWindow : Window 10 | { 11 | Player _play; 12 | 13 | public MainWindow() 14 | { 15 | InitializeComponent(); 16 | this.Opened += MainWindow_Opened; 17 | } 18 | 19 | private async void MainWindow_Opened(object sender, EventArgs e) 20 | { 21 | var b = new BackendPrograms(); 22 | if (System.IO.File.Exists(Properties.Settings.Default.MPlayerPath) == false 23 | && System.IO.File.Exists(b.MPlayer) == false) 24 | { 25 | var dlg = new PlayerProperties(); 26 | await dlg.ShowDialog(this); 27 | } 28 | 29 | this._play = PlayerFactory.Get(videoWidget.Handle.ToInt64(), Properties.Settings.Default.MPlayerPath); 30 | this._play.VideoExited += new MplayerEventHandler(play_VideoExited); 31 | } 32 | 33 | public async void buttonPlay_Click(object sender, RoutedEventArgs e) 34 | { 35 | if (this._play.CurrentStatus != MediaStatus.Stopped) 36 | { 37 | this._play.Stop(); 38 | } 39 | 40 | var openFileDialog = new OpenFileDialog 41 | { 42 | AllowMultiple = false 43 | }; 44 | 45 | var result = await openFileDialog.ShowAsync(this); 46 | if (result == null || result.Length == 0) 47 | { 48 | return; 49 | } 50 | 51 | string filePath = result[0].ToString(); 52 | if (string.IsNullOrWhiteSpace(filePath)) 53 | { 54 | return; 55 | } 56 | 57 | this._play.Play(filePath); 58 | } 59 | 60 | private void play_VideoExited(object sender, MplayerEvent e) 61 | { 62 | this._play.Stop(); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Desktop.UI/Majorsilence.Media.Desktop.UI.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | WinExe 4 | net6.0;net8.0;net9.0 5 | enable 6 | 7 | copyused 8 | true 9 | app.manifest 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | True 35 | True 36 | Settings.settings 37 | 38 | 39 | 40 | 41 | SettingsSingleFileGenerator 42 | Settings.Designer.cs 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/Majorsilence.Media.Desktop.UI/PlayerProperties.axaml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Majorsilence.Media.Desktop.UI/PlayerProperties.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Interactivity; 4 | using Avalonia.Markup.Xaml; 5 | 6 | namespace Majorsilence.Media.Desktop.UI; 7 | 8 | public partial class PlayerProperties : Window 9 | { 10 | public PlayerProperties() 11 | { 12 | InitializeComponent(); 13 | } 14 | 15 | private async void OnBrowseButtonClick(object? sender, RoutedEventArgs e) 16 | { 17 | var openFileDialog = new OpenFileDialog 18 | { 19 | AllowMultiple = false 20 | }; 21 | 22 | var result = await openFileDialog.ShowAsync(this); 23 | if (result != null && result.Length > 0) 24 | { 25 | PlayerPathTextBox.Text = result[0]; 26 | } 27 | } 28 | 29 | private void OnSaveButtonClick(object? sender, RoutedEventArgs e) 30 | { 31 | Properties.Settings.Default.MPlayerPath = PlayerPathTextBox.Text.Trim(); 32 | Properties.Settings.Default.Save(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Majorsilence.Media.Desktop.UI/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using System; 3 | 4 | namespace Majorsilence.Media.Desktop.UI 5 | { 6 | class Program 7 | { 8 | // Initialization code. Don't use any Avalonia, third-party APIs or any 9 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 10 | // yet and stuff might break. 11 | [STAThread] 12 | public static void Main(string[] args) 13 | { 14 | string path = ""; 15 | for (int i = 0; i <= args.Length - 1; i++) 16 | { 17 | if (args[i] == "-path") 18 | { 19 | path = Environment.GetCommandLineArgs()[i + 1].Trim(); 20 | } 21 | } 22 | 23 | if (!string.IsNullOrWhiteSpace(path)) 24 | { 25 | Properties.Settings.Default.MPlayerPath = path; 26 | Properties.Settings.Default.Save(); 27 | } 28 | 29 | BuildAvaloniaApp() 30 | .StartWithClassicDesktopLifetime(args); 31 | } 32 | 33 | // Avalonia configuration, don't remove; also used by visual designer. 34 | public static AppBuilder BuildAvaloniaApp() 35 | => AppBuilder.Configure() 36 | .UsePlatformDetect() 37 | .LogToTrace(); 38 | } 39 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Desktop.UI/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Majorsilence.Media.Desktop.UI.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.12.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | 26 | [global::System.Configuration.UserScopedSettingAttribute()] 27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 28 | [global::System.Configuration.DefaultSettingValueAttribute("")] 29 | public string MPlayerPath { 30 | get { 31 | return ((string)(this["MPlayerPath"])); 32 | } 33 | set { 34 | this["MPlayerPath"] = value; 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Majorsilence.Media.Desktop.UI/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/Majorsilence.Media.Desktop.UI/VideoControl.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Avalonia.Controls; 3 | using Avalonia.Platform; 4 | 5 | namespace Majorsilence.Media.Desktop.UI; 6 | 7 | public class VideoControl : NativeControlHost 8 | { 9 | public IntPtr Handle { get; private set; } 10 | 11 | protected override IPlatformHandle CreateNativeControlCore(IPlatformHandle parent) 12 | { 13 | var handle = base.CreateNativeControlCore(parent); 14 | Handle = handle.Handle; 15 | return handle; 16 | } 17 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Desktop.UI/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 54 | 62 | 63 | 64 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /src/Majorsilence.Media.Images/ImageResize.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2011 (C) Peter Gill 4 | 5 | This file is part of LibImages. 6 | 7 | LibImages is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | LibImages is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with LibImages. If not, see . 19 | 20 | */ 21 | 22 | using SixLabors.ImageSharp; 23 | using SixLabors.ImageSharp.Processing; 24 | 25 | namespace Majorsilence.Media.Images; 26 | 27 | public class ImageResize 28 | { 29 | /// 30 | /// Resize image and keep proportions 31 | /// 32 | /// string - path to file 33 | /// int 34 | /// int 35 | /// Image 36 | public static Image Resize(string file, int width, int height) 37 | { 38 | var image = Image.Load(file); 39 | 40 | image.Mutate(x => x 41 | .Resize(width, height)); 42 | return image; 43 | } 44 | 45 | /// 46 | /// Resize image and keep proportions 47 | /// 48 | /// Image 49 | /// int 50 | /// int 51 | /// Image 52 | public static Image Resize(Image imgToResize, int width, int height) 53 | { 54 | imgToResize.Mutate(x => x 55 | .Resize(new ResizeOptions 56 | { 57 | Size = new Size(width, height), 58 | Mode = ResizeMode.Stretch 59 | })); 60 | return imgToResize; 61 | } 62 | 63 | 64 | public static Image ResizeBlackBar(string file, int width, int height) 65 | { 66 | var image = Image.Load(file); 67 | return ResizeBlackBar(image, width, height); 68 | } 69 | 70 | /// 71 | /// Resize image and keep proportions 72 | /// 73 | /// Image 74 | /// int 75 | /// int 76 | /// Image 77 | public static Image ResizeBlackBar(Image imgToResize, int width, int height) 78 | { 79 | if (imgToResize == null) return null; 80 | 81 | imgToResize.Mutate(x => x 82 | .Resize(new ResizeOptions 83 | { 84 | Size = new Size(width, height), 85 | Mode = ResizeMode.Pad 86 | })); 87 | return imgToResize; 88 | } 89 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Images/Majorsilence.Media.Images.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net6.0;net8.0;net9.0 4 | Majorsilence.Media.Images 5 | Majorsilence.Media.Images 6 | true 7 | True 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/DiscoverTestCaseSource.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using Majorsilence.Media.Videos; 3 | using NUnit.Framework; 4 | 5 | namespace MplayerUnitTests; 6 | 7 | public static class DiscoverTestCaseSource 8 | { 9 | public static IEnumerable TestCasesVideo1 10 | { 11 | get 12 | { 13 | yield return new TestCaseData(new MPlayerDiscover(GlobalVariables.Video1Path, GlobalVariables.MplayerPath)); 14 | #if !MACOS 15 | yield return new TestCaseData(new MpvDiscover(GlobalVariables.Video1Path, GlobalVariables.LibMpvPath)); 16 | #endif 17 | yield return new TestCaseData(new FfmpegDiscover(GlobalVariables.Video1Path, GlobalVariables.FfprobePath)); 18 | } 19 | } 20 | 21 | public static IEnumerable TestCasesVideo2 22 | { 23 | get 24 | { 25 | yield return new TestCaseData(new MPlayerDiscover(GlobalVariables.Video2Path, GlobalVariables.MplayerPath)); 26 | #if !MACOS 27 | yield return new TestCaseData(new MpvDiscover(GlobalVariables.Video2Path, GlobalVariables.LibMpvPath)); 28 | #endif 29 | yield return new TestCaseData(new FfmpegDiscover(GlobalVariables.Video2Path, GlobalVariables.FfprobePath)); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/Discover_Test.cs: -------------------------------------------------------------------------------- 1 | using Majorsilence.Media.Videos; 2 | using NUnit.Framework; 3 | 4 | namespace MplayerUnitTests; 5 | 6 | [TestFixture] 7 | public class Discover_Test 8 | { 9 | [Test, TestCaseSource(typeof(DiscoverTestCaseSource), "TestCasesVideo1")] 10 | public void Video1_Test(Discover discover) 11 | { 12 | using (discover) 13 | { 14 | discover.Execute(); 15 | Assert.AreEqual(128, discover.AudioBitrate, "AudioBitrate"); 16 | Assert.AreEqual(true, discover.Audio, "Audio"); 17 | Assert.AreEqual(true, discover.Video, "Video"); 18 | Assert.AreEqual("9:16", discover.AspectRatioString, "AspectRatioString"); 19 | Assert.AreEqual(0.5625f, discover.AspectRatio, "AspectRatio"); 20 | Assert.AreEqual(1, discover.AudioList.Count, "AudioList.Count"); 21 | Assert.AreEqual(44100, discover.AudioSampleRate, "AudioSampleRate"); 22 | Assert.AreEqual(29, discover.FPS, "FPS"); 23 | Assert.AreEqual(1920, discover.Height, "Height"); 24 | Assert.AreEqual(1080, discover.Width, "Width"); 25 | Assert.AreEqual(2, discover.Length, "Length"); 26 | Assert.AreEqual(3964, discover.VideoBitrate, "VideoBitrate"); 27 | } 28 | } 29 | 30 | [Test, TestCaseSource(typeof(DiscoverTestCaseSource), "TestCasesVideo2")] 31 | public void Video2_Test(Discover discover) 32 | { 33 | using (discover) 34 | { 35 | discover.Execute(); 36 | Assert.AreEqual(128, discover.AudioBitrate, "AudioBitrate"); 37 | Assert.AreEqual(true, discover.Audio, "Audio"); 38 | Assert.AreEqual(true, discover.Video, "Video"); 39 | Assert.AreEqual(1.77777779f, discover.AspectRatio, "AspectRatio"); 40 | Assert.AreEqual(1, discover.AudioList.Count, "AudioList.Count"); 41 | Assert.AreEqual(44100, discover.AudioSampleRate, "AudioSampleRate"); 42 | Assert.AreEqual(30, discover.FPS, "FPS"); 43 | Assert.AreEqual(1080, discover.Height, "Height"); 44 | Assert.AreEqual(1920, discover.Width, "Width"); 45 | Assert.AreEqual(4, discover.Length, "Length"); 46 | Assert.AreEqual(4650, discover.VideoBitrate, "VideoBitrate"); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/GlobalVariables.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace MplayerUnitTests; 5 | 6 | public class GlobalVariables 7 | { 8 | // Test videos can be downloaded from: 9 | // http://files.majorsilence.com/TestVideos.zip 10 | // TODO: You must point the path to the mplayer.exe and the test videos to the location 11 | // were they exist on your computer. 12 | 13 | public static string BasePath => Path.Combine(Path.GetTempPath(), "MPlayerControl", "Tests", "TestVideos"); 14 | 15 | private static string _mplayerPath; 16 | 17 | public static string MplayerPath 18 | { 19 | get 20 | { 21 | InitPath(); 22 | return _mplayerPath; 23 | } 24 | private set { _mplayerPath = value; } 25 | } 26 | 27 | private static string _libMpvPath; 28 | 29 | public static string LibMpvPath 30 | { 31 | get 32 | { 33 | InitPath(); 34 | return _libMpvPath; 35 | } 36 | private set { _libMpvPath = value; } 37 | } 38 | 39 | private static string _ffmpegPath; 40 | 41 | public static string FfmpegPath 42 | { 43 | get 44 | { 45 | InitPath(); 46 | return _ffmpegPath; 47 | } 48 | private set { _ffmpegPath = value; } 49 | } 50 | 51 | private static string _ffprobePath; 52 | 53 | public static string FfprobePath 54 | { 55 | get 56 | { 57 | InitPath(); 58 | return _ffprobePath; 59 | } 60 | private set { _ffprobePath = value; } 61 | } 62 | 63 | public static string Video2Path => Path.Combine(BasePath, "video2.mp4"); 64 | 65 | public static string Video1Path => Path.Combine(BasePath, "video1.mp4"); 66 | 67 | public static string OutputVideoWebM => Path.Combine(BasePath, "OutputVideoWebM.webm"); 68 | 69 | public static string OutputVideoX264 => Path.Combine(BasePath, "OutputVideoX264.mp4"); 70 | 71 | public static string OutputVideoX265 => Path.Combine(BasePath, "OutputVideoX265.mp4"); 72 | 73 | public static string OutputVideoAv1 => Path.Combine(BasePath, "OutputVideoAv1.mp4"); 74 | public static string OutputThumbnail => Path.Combine(BasePath, "OutputThumbnail.jpg"); 75 | public static string OutputVideoDvdMpegPal => Path.Combine(BasePath, "OutputVideoDvdMpegPal.mpg"); 76 | 77 | public static string OutputVideoDvdMpegNtsc => Path.Combine(BasePath, "OutputVideoDvdMpegNtsc.mpg"); 78 | 79 | private static bool initCalled = false; 80 | 81 | private static void InitPath() 82 | { 83 | if (initCalled) return; 84 | 85 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 86 | { 87 | MplayerPath = @"mplayer.exe"; 88 | LibMpvPath = @"mpv-1.dll"; 89 | FfmpegPath = @"ffmpeg.exe"; 90 | FfprobePath = @"ffprobe.exe"; 91 | } 92 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) 93 | { 94 | MplayerPath = "/opt/homebrew/bin/mplayer"; 95 | LibMpvPath = "/opt/homebrew/lib/libmpv.2.dylib"; 96 | FfmpegPath = "/opt/homebrew/bin/ffmpeg"; 97 | FfprobePath = "/opt/homebrew/bin/ffprobe"; 98 | } 99 | else 100 | { 101 | MplayerPath = "mplayer"; 102 | LibMpvPath = "/usr/lib/x86_64-linux-gnu/libmpv.so.1"; 103 | FfmpegPath = "/usr/bin/ffmpeg"; 104 | FfprobePath = "/usr/bin/ffprobe"; 105 | } 106 | 107 | initCalled = true; 108 | } 109 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/ImageResize_Test.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Majorsilence.Media.Images; 3 | using NUnit.Framework; 4 | using SixLabors.ImageSharp; 5 | using SixLabors.ImageSharp.Formats.Jpeg; 6 | 7 | namespace MplayerUnitTests; 8 | 9 | [TestFixture] 10 | public class ImageResize_Test 11 | { 12 | [Test] 13 | public void Test1() 14 | { 15 | var a = ImageResize.ResizeBlackBar( 16 | Path.Combine(GlobalVariables.BasePath, 17 | "1.jpg"), 720, 480); 18 | 19 | var jpgEncoder = new JpegEncoder(); 20 | a.SaveAsJpeg(Path.Combine(GlobalVariables.BasePath, 21 | "test.jpg"), jpgEncoder); 22 | } 23 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/MPlayer_Test.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Linq; 3 | using Majorsilence.Media.Videos; 4 | using NUnit.Framework; 5 | 6 | namespace MplayerUnitTests; 7 | 8 | [TestFixture] 9 | public class MPlayer_Test 10 | { 11 | [Test] 12 | public void MemoryLeak_Test() 13 | { 14 | for (var i = 0; i < 500; i++) 15 | using (var a = new MPlayer(-1, MplayerBackends.ASCII, GlobalVariables.MplayerPath)) 16 | { 17 | a.Play(GlobalVariables.Video1Path); 18 | } 19 | 20 | var processes = Process.GetProcessesByName("mplayer"); 21 | Assert.That(processes.Any(), Is.EqualTo(false), $"mplayer process count is {processes.Count()}"); 22 | } 23 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/Majorsilence.Media.NunitTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net9.0 4 | MplayerUnitTests 5 | 6 | 7 | full 8 | bin\$(Configuration)\ 9 | 10 | 11 | 12 | 13 | 14 | false 15 | 16 | 17 | pdbonly 18 | bin\$(Configuration)\ 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | $(DefineConstants);MACOS 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | PreserveNewest 39 | 40 | 41 | PreserveNewest 42 | 43 | 44 | PreserveNewest 45 | 46 | 47 | PreserveNewest 48 | 49 | 50 | 51 | 52 | all 53 | runtime; build; native; contentfiles; analyzers; buildtransitive 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/Mencoder2_Test.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Majorsilence.Media.Videos; 3 | using NUnit.Framework; 4 | 5 | namespace MplayerUnitTests; 6 | 7 | [TestFixture] 8 | public class Mencoder2_Test 9 | { 10 | [Test] 11 | public void Convert2WebMTest() 12 | { 13 | using (var a = new Mencoder()) 14 | { 15 | a.PercentCompleted += (s, e) => 16 | { 17 | // Console.WriteLine ($"Convert2WebMTest Percent: {e.Value}"); 18 | }; 19 | a.Convert2WebM(GlobalVariables.Video1Path, GlobalVariables.OutputVideoWebM); 20 | } 21 | } 22 | 23 | [Test] 24 | public async Task Convert2WebMAsyncTest() 25 | { 26 | var a = new Mencoder(); 27 | a.PercentCompleted += (s, e) => 28 | { 29 | // Console.WriteLine ($"Convert2WebMAsyncTest Percent: {e.Value}"); 30 | }; 31 | await a.Convert2WebMAsync(GlobalVariables.Video1Path, GlobalVariables.OutputVideoWebM); 32 | } 33 | 34 | 35 | [Test] 36 | public void Convert2X264Test() 37 | { 38 | using (var a = new Mencoder()) 39 | { 40 | a.Convert2X264(GlobalVariables.Video1Path, GlobalVariables.OutputVideoX264); 41 | } 42 | } 43 | 44 | [Test] 45 | public async Task Convert2X264AsyncTest() 46 | { 47 | using (var a = new Mencoder()) 48 | { 49 | await a.Convert2X264Async(GlobalVariables.Video1Path, GlobalVariables.OutputVideoX264); 50 | } 51 | } 52 | 53 | [Test] 54 | public void Convert2DvdMpegPalTest() 55 | { 56 | using (var a = new Mencoder()) 57 | { 58 | a.Convert2DvdMpeg(RegionType.PAL, GlobalVariables.Video1Path, GlobalVariables.OutputVideoDvdMpegPal); 59 | } 60 | } 61 | 62 | [Test] 63 | public async Task Convert2DvdMpegPalAsyncTest() 64 | { 65 | using (var a = new Mencoder()) 66 | { 67 | await a.Convert2DvdMpegAsync(RegionType.PAL, GlobalVariables.Video1Path, 68 | GlobalVariables.OutputVideoDvdMpegPal); 69 | } 70 | } 71 | 72 | [Test] 73 | public void Convert2DvdMpegNtscTest() 74 | { 75 | using (var a = new Mencoder()) 76 | { 77 | a.Convert2DvdMpeg(RegionType.NTSC, GlobalVariables.Video1Path, GlobalVariables.OutputVideoDvdMpegNtsc); 78 | 79 | a.ConversionComplete += a_ConversionComplete; 80 | } 81 | } 82 | 83 | [Test] 84 | public async Task Convert2DvdMpegNtscAsyncTest() 85 | { 86 | using (var a = new Mencoder()) 87 | { 88 | await a.Convert2DvdMpegAsync(RegionType.NTSC, GlobalVariables.Video1Path, 89 | GlobalVariables.OutputVideoDvdMpegNtsc); 90 | } 91 | } 92 | 93 | private void a_ConversionComplete(object sender, MplayerEvent e) 94 | { 95 | using (var a = new MPlayerDiscover(GlobalVariables.OutputVideoDvdMpegNtsc, GlobalVariables.MplayerPath)) 96 | { 97 | Assert.AreEqual(192, a.AudioBitrate); 98 | Assert.AreEqual(720, a.Width); 99 | Assert.AreEqual(480, a.Height); 100 | Assert.AreEqual(48000, a.AudioSampleRate); 101 | } 102 | } 103 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | // Setting ComVisible to false makes the types in this assembly not visible 4 | // to COM components. If you need to access a type in this assembly from 5 | // COM, set the ComVisible attribute to true on that type. 6 | [assembly: ComVisible(false)] 7 | 8 | // The following GUID is for the ID of the typelib if this project is exposed to COM 9 | [assembly: Guid("112fd936-6b5e-42c8-9e1b-166986263ad0")] -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/SetupInitialize.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | using System.Runtime.InteropServices; 5 | using NUnit.Framework; 6 | 7 | namespace MplayerUnitTests; 8 | 9 | [SetUpFixture] 10 | public class SetupInitialize 11 | { 12 | private static string finalPath; 13 | 14 | [OneTimeSetUp] 15 | public void RunBeforeAnyTests() 16 | { 17 | finalPath = GlobalVariables.BasePath; 18 | FinalTearDown(); 19 | if (!Directory.Exists(finalPath)) Directory.CreateDirectory(finalPath); 20 | 21 | var files = Directory.GetFiles(Path.Combine(ExecutingDirectory(), "TestVideos")); 22 | foreach (var file in files) File.Copy(file, Path.Combine(finalPath, Path.GetFileName(file))); 23 | } 24 | 25 | [OneTimeTearDown] 26 | public void FinalTearDown() 27 | { 28 | if (Directory.Exists(finalPath)) Directory.Delete(finalPath, true); 29 | } 30 | 31 | private static string ExecutingDirectory() 32 | { 33 | var location = Assembly.GetExecutingAssembly().Location; 34 | location = Path.GetDirectoryName(location); 35 | return location; 36 | } 37 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/SlideShow_Test.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Threading.Tasks; 4 | using Majorsilence.Media.Videos; 5 | using NUnit.Framework; 6 | 7 | namespace MplayerUnitTests; 8 | 9 | [TestFixture] 10 | public class SlideShow_Test 11 | { 12 | [Test] 13 | public void Test1() 14 | { 15 | var a = new SlideShow(); 16 | 17 | var b = new List(); 18 | b.Add(new SlideShowInfo(Path.Combine(GlobalVariables.BasePath, 19 | "1.jpg"), 20 | SlideShowEffect.TimeWarp)); 21 | b.Add(new SlideShowInfo(Path.Combine(GlobalVariables.BasePath, 22 | "2.jpg"), 23 | SlideShowEffect.Moire)); 24 | b.Add(new SlideShowInfo(Path.Combine(GlobalVariables.BasePath, 25 | "3.jpg"), 26 | SlideShowEffect.Water)); 27 | b.Add(new SlideShowInfo(Path.Combine(GlobalVariables.BasePath, 28 | "4.jpg"), 29 | SlideShowEffect.RandomJitter)); 30 | b.Add(new SlideShowInfo(Path.Combine(GlobalVariables.BasePath, 31 | "5.jpg"), 32 | SlideShowEffect.Pixelate)); 33 | a.CreateSlideShow(b, 34 | Path.Combine(GlobalVariables.BasePath, "helloworld.mpg"), 35 | Path.Combine(GlobalVariables.BasePath, @"doxent_-_Sunset_Boulevard-edit.mp3"), 36 | 5); 37 | } 38 | 39 | [Test] 40 | public async Task Async_Test1() 41 | { 42 | var a = new SlideShow(); 43 | 44 | var b = new List(); 45 | b.Add(new SlideShowInfo(Path.Combine(GlobalVariables.BasePath, 46 | "1.jpg"), 47 | SlideShowEffect.TimeWarp)); 48 | b.Add(new SlideShowInfo(Path.Combine(GlobalVariables.BasePath, 49 | "2.jpg"), 50 | SlideShowEffect.Moire)); 51 | b.Add(new SlideShowInfo(Path.Combine(GlobalVariables.BasePath, 52 | "3.jpg"), 53 | SlideShowEffect.Water)); 54 | b.Add(new SlideShowInfo(Path.Combine(GlobalVariables.BasePath, 55 | "4.jpg"), 56 | SlideShowEffect.RandomJitter)); 57 | b.Add(new SlideShowInfo(Path.Combine(GlobalVariables.BasePath, 58 | "5.jpg"), 59 | SlideShowEffect.Pixelate)); 60 | await a.CreateSlideShowAsync(b, 61 | Path.Combine(GlobalVariables.BasePath, "helloworld_async_test.mpg"), 62 | Path.Combine(GlobalVariables.BasePath, @"doxent_-_Sunset_Boulevard-edit.mp3"), 63 | 5); 64 | } 65 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/TestVideos/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/src/Majorsilence.Media.NunitTests/TestVideos/1.jpg -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/TestVideos/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/src/Majorsilence.Media.NunitTests/TestVideos/2.jpg -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/TestVideos/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/src/Majorsilence.Media.NunitTests/TestVideos/3.jpg -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/TestVideos/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/src/Majorsilence.Media.NunitTests/TestVideos/4.jpg -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/TestVideos/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/src/Majorsilence.Media.NunitTests/TestVideos/5.jpg -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/TestVideos/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/src/Majorsilence.Media.NunitTests/TestVideos/6.jpg -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/TestVideos/audio_license.txt: -------------------------------------------------------------------------------- 1 | Sunset Boulevard by Doxent Zsigmond (c) copyright 2015 2 | Licensed under a Creative Commons Attribution Noncommercial (3.0) license. 3 | http://dig.ccmixter.org/files/doxent/50485 Ft: Siobhan Dakay, unreal_dm -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/TestVideos/doxent_-_Sunset_Boulevard-edit.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/src/Majorsilence.Media.NunitTests/TestVideos/doxent_-_Sunset_Boulevard-edit.mp3 -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/TestVideos/video1.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/src/Majorsilence.Media.NunitTests/TestVideos/video1.mp4 -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/TestVideos/video2.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/src/Majorsilence.Media.NunitTests/TestVideos/video2.mp4 -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/VideoEncoderTestCaseSource.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using Majorsilence.Media.Videos; 3 | using NUnit.Framework; 4 | 5 | namespace MplayerUnitTests; 6 | 7 | public static class VideoEncoderTestCaseSource 8 | { 9 | public static IEnumerable TestCasesEncoders 10 | { 11 | get 12 | { 13 | yield return new TestCaseData(new Mencoder(GlobalVariables.MplayerPath)); 14 | yield return new TestCaseData(new Ffmpeg(GlobalVariables.FfprobePath)); 15 | } 16 | } 17 | 18 | public static IEnumerable TestCasesEncodersConvertAsync 19 | { 20 | get 21 | { 22 | yield return new TestCaseData(new Mencoder(GlobalVariables.MplayerPath), VideoType.vp9, AudioType.opus, 23 | VideoAspectRatios.p240); 24 | yield return new TestCaseData(new Mencoder(GlobalVariables.MplayerPath), VideoType.x264, AudioType.aac, 25 | VideoAspectRatios.p240); 26 | yield return new TestCaseData(new Ffmpeg(GlobalVariables.FfprobePath), VideoType.vp9, AudioType.opus, 27 | VideoAspectRatios.p240); 28 | yield return new TestCaseData(new Ffmpeg(GlobalVariables.FfprobePath), VideoType.x264, AudioType.aac, 29 | VideoAspectRatios.p240); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.NunitTests/VideoEncoder_Test.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Majorsilence.Media.Videos; 5 | using NUnit.Framework; 6 | 7 | namespace MplayerUnitTests; 8 | 9 | [TestFixture] 10 | public class VideoEncoder_Test 11 | { 12 | [Test, TestCaseSource(typeof(VideoEncoderTestCaseSource), "TestCasesEncoders")] 13 | public void Convert2WebMTest(IVideoEncoder encoder) 14 | { 15 | using (encoder as IDisposable) 16 | { 17 | encoder.Convert2WebM(GlobalVariables.Video1Path, GlobalVariables.OutputVideoWebM); 18 | } 19 | } 20 | 21 | [Test, TestCaseSource(typeof(VideoEncoderTestCaseSource), "TestCasesEncoders")] 22 | public void Convert2X264Test(IVideoEncoder encoder) 23 | { 24 | using (encoder as IDisposable) 25 | { 26 | encoder.Convert2X264(GlobalVariables.Video1Path, GlobalVariables.OutputVideoX264); 27 | } 28 | } 29 | 30 | [Test, TestCaseSource(typeof(VideoEncoderTestCaseSource), "TestCasesEncoders")] 31 | public void Convert2X265Test(IVideoEncoder encoder) 32 | { 33 | using (encoder as IDisposable) 34 | { 35 | encoder.Convert2X265(GlobalVariables.Video1Path, GlobalVariables.OutputVideoX265); 36 | } 37 | } 38 | 39 | [Test, TestCaseSource(typeof(VideoEncoderTestCaseSource), "TestCasesEncoders")] 40 | public void Convert2Av1Test(IVideoEncoder encoder) 41 | { 42 | using (encoder as IDisposable) 43 | { 44 | encoder.Convert2Av1(GlobalVariables.Video1Path, GlobalVariables.OutputVideoAv1); 45 | } 46 | } 47 | 48 | [Test, TestCaseSource(typeof(VideoEncoderTestCaseSource), "TestCasesEncoders")] 49 | public async Task ThumbnailTest(IVideoEncoder encoder) 50 | { 51 | using (encoder as IDisposable) 52 | { 53 | await encoder.ThumbnailAsync(GlobalVariables.Video1Path, GlobalVariables.OutputThumbnail, 54 | CancellationToken.None); 55 | } 56 | } 57 | 58 | [Test, TestCaseSource(typeof(VideoEncoderTestCaseSource), "TestCasesEncodersConvertAsync")] 59 | public async Task ConvertAsync(IVideoEncoder encoder, VideoType videoType, AudioType audioType, 60 | VideoAspectRatios aspectRatio) 61 | { 62 | using (encoder as IDisposable) 63 | { 64 | await encoder.ConvertAsync(videoType, audioType, aspectRatio, GlobalVariables.Video1Path, 65 | GlobalVariables.OutputVideoWebM, 66 | CancellationToken.None); 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Videos/AspectRatio.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2010 (C) Peter Gill 4 | 5 | This file is part of Majorsilence.Media.Videos. 6 | 7 | Majorsilence.Media.Videos is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | Majorsilence.Media.Videos is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | namespace Majorsilence.Media.Videos; 23 | 24 | /// 25 | /// This class is used to hold different video aspect ratio values. 26 | /// 27 | public class ScreenAspectRatio 28 | { 29 | /// 30 | /// The float value of 4/3 aspect ratio. 31 | /// 32 | public static float FourThree => 4.0f / 3.0f; 33 | 34 | /// 35 | /// The float value of 16/9 aspect ratio. 36 | /// 37 | public static float SixteenNine => 16.0f / 9.0f; 38 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Videos/AudioType.cs: -------------------------------------------------------------------------------- 1 | namespace Majorsilence.Media.Videos; 2 | 3 | public enum AudioType 4 | { 5 | implementation_detail, 6 | ac3, 7 | mp3, 8 | mp2, 9 | vorbis, 10 | flac, 11 | wmav1, 12 | wmav2, 13 | opus, 14 | aac 15 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Videos/BackendPrograms.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2010 (C) Peter Gill 4 | 5 | This file is part of Majorsilence.Media.Videos. 6 | 7 | Majorsilence.Media.Videos is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | Majorsilence.Media.Videos is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | using System; 23 | using System.IO; 24 | using System.Reflection; 25 | 26 | namespace Majorsilence.Media.Videos; 27 | 28 | public class BackendPrograms 29 | { 30 | private readonly string _mencoderPath; 31 | 32 | private readonly string _mplayerPath; 33 | 34 | public BackendPrograms() 35 | { 36 | _mplayerPath = ""; 37 | _mencoderPath = ""; 38 | } 39 | 40 | public BackendPrograms(string mplayerPath) 41 | { 42 | _mplayerPath = mplayerPath; 43 | _mencoderPath = ""; 44 | } 45 | 46 | public BackendPrograms(string mplayerPath, string mencoderPath) 47 | { 48 | _mplayerPath = mplayerPath; 49 | _mencoderPath = mencoderPath; 50 | } 51 | 52 | public string MPlayer 53 | { 54 | get 55 | { 56 | if (OSPlatform() == "windows") 57 | { 58 | if (_mplayerPath != "") return _mplayerPath; 59 | return Path.Combine(CurrentAssemblyDirectory(), "mplayer.exe"); 60 | } 61 | 62 | if (_mplayerPath != "") return _mplayerPath; 63 | 64 | return "mplayer"; 65 | } 66 | } 67 | 68 | public string MEncoder 69 | { 70 | get 71 | { 72 | if (OSPlatform() == "windows") 73 | { 74 | if (_mencoderPath != "") return _mencoderPath; 75 | 76 | return Path.Combine(CurrentAssemblyDirectory(), "mencoder.exe"); 77 | } 78 | 79 | if (_mencoderPath != "") return _mencoderPath; 80 | 81 | return "mencoder"; 82 | } 83 | } 84 | 85 | 86 | public static string OSPlatform() 87 | { 88 | if (Environment.OSVersion.Platform == PlatformID.Unix || 89 | Environment.OSVersion.Platform == PlatformID.MacOSX) return "unix"; 90 | return "windows"; 91 | } 92 | 93 | /// 94 | /// Return the directory of the current executing assembly 95 | /// 96 | /// 97 | private static string CurrentAssemblyDirectory() 98 | { 99 | var location = Assembly.GetExecutingAssembly().Location; 100 | location = Path.GetDirectoryName(location); 101 | return location; 102 | } 103 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Videos/Discover.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | namespace Majorsilence.Media.Videos; 6 | 7 | public interface Discover : IDisposable 8 | { 9 | int VideoBitrate { get; } 10 | 11 | int AudioBitrate { get; } 12 | 13 | int AudioSampleRate { get; } 14 | 15 | int Width { get; } 16 | 17 | int Height { get; } 18 | 19 | int FPS { get; } 20 | 21 | int Length { get; } 22 | 23 | string AspectRatioString { get; } 24 | float AspectRatio { get; } 25 | 26 | List AudioList { get; } 27 | 28 | List AudioTracks { get; } 29 | 30 | bool Video { get; } 31 | 32 | bool Audio { get; } 33 | 34 | List SubtitleList { get; } 35 | 36 | void Execute(); 37 | 38 | Task ExecuteAsync(); 39 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Videos/DiscoverFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Majorsilence.Media.Videos; 4 | 5 | public static class DiscoverFactory 6 | { 7 | /// 8 | /// Attempt to auto detect prefered backends. 9 | /// 10 | /// The multimedia file to discover 11 | /// Path to mplayer executable or mpv library 12 | public static Discover Get(string file, string path) 13 | { 14 | if (PlatformCheck.RunningPlatform() == Platform.Windows) 15 | return Windows(file, path); 16 | if (PlatformCheck.RunningPlatform() == Platform.Linux) 17 | return Linux(file, path); 18 | if (PlatformCheck.RunningPlatform() == Platform.Mac) 19 | return Mac(file, path); 20 | throw new NotImplementedException(); 21 | } 22 | 23 | private static Discover Windows(string file, string path) 24 | { 25 | if (path.Contains("mplayer")) 26 | return new MPlayerDiscover(file, path); 27 | if (path.Contains("mpv")) return new MpvDiscover(file, path); 28 | 29 | return null; 30 | } 31 | 32 | private static Discover Linux(string file, string path) 33 | { 34 | if (path.Contains("mplayer")) 35 | return new MPlayerDiscover(file, path); 36 | if (path.Contains("libmpv")) return new MpvDiscover(file, path); 37 | 38 | return null; 39 | } 40 | 41 | private static Discover Mac(string file, string path) 42 | { 43 | if (path.Contains("mplayer")) 44 | return new MPlayerDiscover(file, path); 45 | if (path.Contains("libmpv")) return new MpvDiscover(file, path); 46 | 47 | return null; 48 | } 49 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Videos/Exceptions/MPlayerControlException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Majorsilence.Media.Videos.Exceptions; 4 | 5 | public class MPlayerControlException : Exception 6 | { 7 | public int ErrorCode; 8 | 9 | public MPlayerControlException(string msg) 10 | : base(msg) 11 | { 12 | } 13 | 14 | public MPlayerControlException(string msg, int errorCode) 15 | : base(msg) 16 | { 17 | ErrorCode = errorCode; 18 | } 19 | 20 | public MPlayerControlException(string msg, Exception ex) 21 | : base(msg, ex) 22 | { 23 | } 24 | 25 | public MPlayerControlException(string msg, Exception ex, int errorCode) 26 | : base(msg, ex) 27 | { 28 | ErrorCode = errorCode; 29 | } 30 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Videos/Globals.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2010 (C) Peter Gill 4 | 5 | This file is part of Majorsilence.Media.Videos. 6 | 7 | Majorsilence.Media.Videos is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | Majorsilence.Media.Videos is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | using System; 23 | using System.Globalization; 24 | using System.IO; 25 | 26 | namespace Majorsilence.Media.Videos; 27 | 28 | public class Globals 29 | { 30 | private Globals() 31 | { 32 | } 33 | 34 | public static string MajorSilenceLocalAppDataDirectory 35 | { 36 | get 37 | { 38 | string msDir = null; 39 | msDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); 40 | msDir = Path.Combine(msDir, "MajorSilence"); 41 | return msDir; 42 | } 43 | } 44 | 45 | public static string MajorSilenceMPlayerLocalAppDataDirectory => 46 | Path.Combine(MajorSilenceLocalAppDataDirectory, "MPlayer"); 47 | 48 | public static string MajorSilenceMEncoderLocalAppDataDirectory => 49 | Path.Combine(MajorSilenceLocalAppDataDirectory, "MEncoder"); 50 | 51 | public static int IntParse(string input) 52 | { 53 | return int.Parse(input.Replace(",", "."), CultureInfo.InvariantCulture); 54 | } 55 | 56 | public static float FloatParse(string input) 57 | { 58 | input = input.Trim(); 59 | if (input.Equals("nan", StringComparison.OrdinalIgnoreCase) || 60 | input.Equals("-nan", StringComparison.OrdinalIgnoreCase)) return float.NaN; 61 | var outValue = 0f; 62 | float.TryParse(input.Replace(",", "."), out outValue); 63 | return outValue; 64 | } 65 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Videos/IVideoEncoder.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2012 (C) Peter Gill 4 | 5 | This file is part of Majorsilence.Media.Videos. 6 | 7 | Majorsilence.Media.Videos is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | Majorsilence.Media.Videos is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | using System.Threading; 23 | using System.Threading.Tasks; 24 | 25 | namespace Majorsilence.Media.Videos; 26 | 27 | public interface IVideoEncoder 28 | { 29 | void Convert(string cmd, string workingDirectory = ""); 30 | Task ConvertAsync(string cmd, string workingDirectory, CancellationToken stoppingToken); 31 | void Convert(VideoType vidType, AudioType audType, string videoToConvertFilePath, string outputFilePath); 32 | 33 | void Convert(VideoType vidType, AudioType audType, VideoAspectRatios aspectRatios, 34 | string videoToConvertFilePath, 35 | string outputFilePath); 36 | 37 | Task ConvertAsync(VideoType vidType, AudioType audType, VideoAspectRatios aspectRatios, 38 | string videoToConvertFilePath, 39 | string outputFilePath, CancellationToken stoppingToken); 40 | 41 | void Convert2WebM(string videoToConvertFilePath, string outputFilePath); 42 | void Convert2X264(string videoToConvertFilePath, string outputFilePath); 43 | void Convert2X265(string videoToConvertFilePath, string outputFilePath); 44 | void Convert2Av1(string videoToConvertFilePath, string outputFilePath); 45 | Task ThumbnailAsync(string videoToConvertFilePath, string outputFilePath, CancellationToken stoppingToken); 46 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Videos/Logging.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2010 (C) Peter Gill 4 | 5 | This file is part of Majorsilence.Media.Videos. 6 | 7 | Majorsilence.Media.Videos is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | Majorsilence.Media.Videos is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | using System; 23 | using System.Diagnostics; 24 | using System.IO; 25 | 26 | namespace Majorsilence.Media.Videos; 27 | 28 | public class Logging 29 | { 30 | // TODO: replace with real logging framework 31 | 32 | private static volatile Logging instance; 33 | private static readonly object syncRoot = new(); 34 | 35 | private readonly string filePath; 36 | 37 | 38 | private Logging() 39 | { 40 | filePath = Path.Combine(Globals.MajorSilenceMPlayerLocalAppDataDirectory, "MajorSilence-Debug.txt"); 41 | Trace.Listeners.Add(new TextWriterTraceListener(filePath)); 42 | Trace.AutoFlush = true; 43 | } 44 | 45 | public static Logging Instance 46 | { 47 | get 48 | { 49 | if (instance == null) 50 | lock (syncRoot) 51 | { 52 | if (instance == null) instance = new Logging(); 53 | } 54 | 55 | if (Directory.Exists(Globals.MajorSilenceMPlayerLocalAppDataDirectory) == false) 56 | Directory.CreateDirectory(Globals.MajorSilenceMPlayerLocalAppDataDirectory); 57 | 58 | return instance; 59 | } 60 | } 61 | 62 | public void WriteLine(string msg) 63 | { 64 | WriteLine(msg, "UNKNOWN"); 65 | } 66 | 67 | public void WriteLine(string msg, string category) 68 | { 69 | var output = DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString() + Environment.NewLine; 70 | output += msg + Environment.NewLine; 71 | output += Environment.NewLine + Environment.NewLine; 72 | 73 | Trace.WriteLine(output, category); 74 | } 75 | 76 | public void WriteLine(Exception value) 77 | { 78 | WriteLine(value, "UNKNOWN"); 79 | } 80 | 81 | public void WriteLine(Exception value, string category) 82 | { 83 | WriteLine(value.Message + Environment.NewLine + value.StackTrace, category); 84 | } 85 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Videos/Majorsilence.Media.Videos.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Majorsilence.Media.Videos 4 | net6.0;net8.0;net9.0 5 | Majorsilence.Media.Videos 6 | Majorsilence.Media.Videos 7 | True 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Majorsilence.Media.Videos/MediaStatus.cs: -------------------------------------------------------------------------------- 1 | namespace Majorsilence.Media.Videos; 2 | 3 | /// 4 | /// Status of the mplayer.exe instance. 5 | /// 6 | public enum MediaStatus 7 | { 8 | Paused, 9 | Playing, 10 | Stopped 11 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Videos/MplayerBackends.cs: -------------------------------------------------------------------------------- 1 | namespace Majorsilence.Media.Videos; 2 | 3 | /// 4 | /// The video output backend that mplayer is using. 5 | /// 6 | public enum MplayerBackends 7 | { 8 | /// 9 | /// This may be the recommened backend on Mac OSX. 10 | /// 11 | OpenGL = 1, 12 | 13 | /// 14 | /// Simple Version 15 | /// 16 | GL = 2, 17 | 18 | /// 19 | /// Simple Version. Variant of the OpenGL video output driver. 20 | /// Supports videos larger than the maximum texture size but lacks 21 | /// many of the ad‐vanced features and optimizations of the gl driver 22 | /// and is un‐likely to be extended further. 23 | /// 24 | GL2 = 3, 25 | 26 | /// 27 | /// Windows. This is the recommened backend on windows. 28 | /// 29 | Direct3D = 4, 30 | 31 | /// 32 | /// Windows 33 | /// 34 | DirectX = 5, 35 | 36 | /// 37 | /// Linux 38 | /// 39 | X11 = 6, 40 | VESA = 7, 41 | 42 | /// 43 | /// Mac OS X 44 | /// 45 | Quartz = 8, 46 | 47 | /// 48 | /// Mac OS X 49 | /// 50 | CoreVideo = 9, 51 | 52 | /// 53 | /// Cross Platform 54 | /// 55 | SDL = 10, 56 | 57 | /// 58 | /// Linux 59 | /// 60 | Vdpau = 11, 61 | 62 | /// 63 | /// ASCII art video output driver that works on a text console. 64 | /// 65 | ASCII = 12, 66 | 67 | /// 68 | /// Color ASCII art video output driver that works on a text console. 69 | /// 70 | ColorASCII = 13, 71 | 72 | /// 73 | /// Linux. Play video using the DirectFB library. 74 | /// 75 | Directfb = 14, 76 | 77 | /// 78 | /// Linux. Nintendo Wii/GameCube specific video output driver. 79 | /// 80 | Wii = 15, 81 | 82 | /// 83 | /// Linux. requires Linux 2.6.22+ kernel, Video output driver for 84 | // V4L2 compliant cards with built-in hardware MPEG decoder. 85 | /// 86 | V4l2 = 16, 87 | 88 | /// 89 | /// Linux 90 | /// 91 | XV = 17 92 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Videos/MplayerEvent.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2010 (C) Peter Gill 4 | 5 | This file is part of Majorsilence.Media.Videos. 6 | 7 | Majorsilence.Media.Videos is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | Majorsilence.Media.Videos is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | 23 | using System; 24 | 25 | namespace Majorsilence.Media.Videos; 26 | 27 | /// 28 | /// Delegatefor use with mplayer control events. Uses MplayerEvent. 29 | /// 30 | /// 31 | /// 32 | public delegate void MplayerEventHandler(object sender, MplayerEvent e); 33 | 34 | /// 35 | /// Event class that is used with the mplayer control. Can send String or Integer messages. 36 | /// 37 | public class MplayerEvent : EventArgs 38 | { 39 | public MplayerEvent(string m) 40 | { 41 | Message = m; 42 | Value = 0; 43 | } 44 | 45 | public MplayerEvent(float v) 46 | { 47 | Message = ""; 48 | Value = v; 49 | } 50 | 51 | /// 52 | /// Event Message. 53 | /// 54 | public string Message { get; } 55 | 56 | public float Value { get; } 57 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Videos/PlatformCheck.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Majorsilence.Media.Videos; 5 | 6 | public enum Platform 7 | { 8 | Windows, 9 | Linux, 10 | Mac 11 | } 12 | 13 | public static class PlatformCheck 14 | { 15 | public static Platform RunningPlatform() 16 | { 17 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 18 | return Platform.Windows; 19 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) 20 | return Platform.Linux; 21 | if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return Platform.Mac; 22 | 23 | throw new PlatformNotSupportedException(); 24 | } 25 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Videos/Player.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Majorsilence.Media.Videos; 4 | 5 | public interface Player : IDisposable 6 | { 7 | bool FullScreen { get; set; } 8 | 9 | MediaStatus CurrentStatus { get; set; } 10 | 11 | string CurrentFilePath { get; } 12 | event MplayerEventHandler VideoExited; 13 | event MplayerEventHandler CurrentPosition; 14 | 15 | void ToggleFullScreen(); 16 | 17 | void Stop(); 18 | 19 | void Pause(); 20 | 21 | void Mute(); 22 | 23 | /// 24 | /// The window id that the video will play in 25 | /// 26 | /// Wid. 27 | void SetHandle(long wid); 28 | 29 | void MovePosition(int timePosition); 30 | 31 | void Play(string filePath); 32 | 33 | void Seek(int value, Seek type); 34 | 35 | void SetSize(int width, int height); 36 | 37 | void SwitchAudioTrack(int track); 38 | 39 | void SwitchSubtitle(int sub); 40 | 41 | int CurrentPlayingFileLength(); 42 | 43 | void Volume(int volume); 44 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Videos/PlayerFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Majorsilence.Media.Videos; 4 | 5 | public static class PlayerFactory 6 | { 7 | /// 8 | /// Attempt to auto detect prefered backends. 9 | /// 10 | /// Handle. 11 | /// Path to mplayer executable or mpv library 12 | public static Player Get(long handle, string path) 13 | { 14 | // -path "/usr/lib/x86_64-linux-gnu/libmpv.so.1" 15 | 16 | if (PlatformCheck.RunningPlatform() == Platform.Windows) 17 | return Windows(handle, path); 18 | if (PlatformCheck.RunningPlatform() == Platform.Linux) 19 | return Linux(handle, path); 20 | if (PlatformCheck.RunningPlatform() == Platform.Mac) 21 | return Mac(handle, path); 22 | throw new NotImplementedException(); 23 | } 24 | 25 | private static Player Windows(long handle, string path) 26 | { 27 | if (path.Contains("mplayer")) 28 | return new MPlayer(handle, MplayerBackends.Direct3D, path); 29 | if (path.Contains("mpv")) return new MpvPlayer(handle, path); 30 | 31 | return null; 32 | } 33 | 34 | private static Player Linux(long handle, string path) 35 | { 36 | if (path.Contains("mplayer")) 37 | return new MPlayer(handle, MplayerBackends.XV, path); 38 | if (path.Contains("libmpv")) return new MpvPlayer(handle, path); 39 | 40 | return null; 41 | } 42 | 43 | private static Player Mac(long handle, string path) 44 | { 45 | if (path.Contains("mplayer")) 46 | return new MPlayer(handle, MplayerBackends.OpenGL, path); 47 | if (path.Contains("libmpv")) return new MpvPlayer(handle, path); 48 | 49 | return null; 50 | } 51 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Videos/RegionType.cs: -------------------------------------------------------------------------------- 1 | namespace Majorsilence.Media.Videos; 2 | 3 | /// 4 | /// The region type used in the video. 5 | /// 6 | public enum RegionType 7 | { 8 | NTSC, 9 | PAL 10 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Videos/Seek.cs: -------------------------------------------------------------------------------- 1 | namespace Majorsilence.Media.Videos; 2 | 3 | /// 4 | /// The seek type that is used when seeking a new position in the video stream. 5 | /// 6 | public enum Seek 7 | { 8 | Relative = 0, 9 | Percentage = 1, 10 | Absolute = 2 11 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Videos/TimeConversion.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2010 (C) Peter Gill 4 | 5 | This file is part of Majorsilence.Media.Videos. 6 | 7 | Majorsilence.Media.Videos is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | Majorsilence.Media.Videos is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | using Majorsilence.Media.Videos.Exceptions; 23 | 24 | namespace Majorsilence.Media.Videos; 25 | 26 | public class TimeConversion 27 | { 28 | /// 29 | /// return time in Hours:Minutes:Seconds format. 30 | /// 31 | /// 32 | /// 33 | public static string ConvertTimeHHMMSS(int timeInSeconds) 34 | { 35 | if (timeInSeconds < 0) 36 | throw new MPlayerControlException( 37 | "Invalid time. Seconds must be greated then >= 0. Seconds passed in was: " + timeInSeconds); 38 | 39 | var hours = 0; 40 | var minutes = 0; 41 | var seconds = 0; 42 | var time_string = ""; 43 | 44 | if (timeInSeconds >= 3600) 45 | { 46 | hours = timeInSeconds / 3600; 47 | timeInSeconds = timeInSeconds - hours * 3600; 48 | } 49 | 50 | if (timeInSeconds >= 60) 51 | { 52 | minutes = timeInSeconds / 60; 53 | timeInSeconds = timeInSeconds - minutes * 60; 54 | } 55 | 56 | //remaining time is seconds 57 | seconds = timeInSeconds; 58 | 59 | time_string = time_string + hours.ToString().PadLeft(2, '0') + ":" + 60 | minutes.ToString().PadLeft(2, '0') + ":" + seconds.ToString().PadLeft(2, '0'); 61 | 62 | //return time in Hours:Minutes:Seconds format 63 | return time_string; 64 | } 65 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Videos/VideoType.cs: -------------------------------------------------------------------------------- 1 | namespace Majorsilence.Media.Videos; 2 | 3 | public enum VideoType 4 | { 5 | xvid, 6 | av1, 7 | vp9, 8 | x264, 9 | x265, 10 | wmv1, 11 | wmv2, 12 | mpeg4 13 | } 14 | 15 | public enum VideoAspectRatios 16 | { 17 | original, 18 | p7680, 19 | p2160, 20 | p1440, 21 | p1080, 22 | p720, 23 | p480, 24 | p360, 25 | p240 26 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Web.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.2.32630.192 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Majorsilence.Media.Images", "Majorsilence.Media.Images\Majorsilence.Media.Images.csproj", "{C859FA27-7882-4814-9202-9C9D807C2CE5}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Majorsilence.Media.Videos", "Majorsilence.Media.Videos\Majorsilence.Media.Videos.csproj", "{5D3616D0-160D-4601-AB67-E1ED9111C676}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Majorsilence.Media.NunitTests", "Majorsilence.Media.NunitTests\Majorsilence.Media.NunitTests.csproj", "{5E050A99-68E9-4F34-82BD-4E04EAF64CA7}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Majorsilence.Media.Web", "Majorsilence.Media.Web\Majorsilence.Media.Web.csproj", "{D99A13FE-8E34-4AA7-96A6-5120159220FE}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Majorsilence.Media.WorkerService", "Majorsilence.Media.WorkerService\Majorsilence.Media.WorkerService.csproj", "{701173C2-C7D4-4ECD-B6FE-663424B0BAD7}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Majorsilence.Media.WorkerService.Tests", "Majorsilence.Media.WorkerService.Tests\Majorsilence.Media.WorkerService.Tests.csproj", "{E6FC85A6-15F5-4404-A7D2-6B9AE3D44DAD}" 17 | EndProject 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Any CPU = Debug|Any CPU 21 | Release|Any CPU = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {C859FA27-7882-4814-9202-9C9D807C2CE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {C859FA27-7882-4814-9202-9C9D807C2CE5}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {C859FA27-7882-4814-9202-9C9D807C2CE5}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {C859FA27-7882-4814-9202-9C9D807C2CE5}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {5D3616D0-160D-4601-AB67-E1ED9111C676}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {5D3616D0-160D-4601-AB67-E1ED9111C676}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {5D3616D0-160D-4601-AB67-E1ED9111C676}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {5D3616D0-160D-4601-AB67-E1ED9111C676}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {5E050A99-68E9-4F34-82BD-4E04EAF64CA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {5E050A99-68E9-4F34-82BD-4E04EAF64CA7}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {5E050A99-68E9-4F34-82BD-4E04EAF64CA7}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {5E050A99-68E9-4F34-82BD-4E04EAF64CA7}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {D99A13FE-8E34-4AA7-96A6-5120159220FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {D99A13FE-8E34-4AA7-96A6-5120159220FE}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {D99A13FE-8E34-4AA7-96A6-5120159220FE}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {D99A13FE-8E34-4AA7-96A6-5120159220FE}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {701173C2-C7D4-4ECD-B6FE-663424B0BAD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {701173C2-C7D4-4ECD-B6FE-663424B0BAD7}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {701173C2-C7D4-4ECD-B6FE-663424B0BAD7}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {701173C2-C7D4-4ECD-B6FE-663424B0BAD7}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {E6FC85A6-15F5-4404-A7D2-6B9AE3D44DAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {E6FC85A6-15F5-4404-A7D2-6B9AE3D44DAD}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {E6FC85A6-15F5-4404-A7D2-6B9AE3D44DAD}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {E6FC85A6-15F5-4404-A7D2-6B9AE3D44DAD}.Release|Any CPU.Build.0 = Release|Any CPU 48 | EndGlobalSection 49 | GlobalSection(SolutionProperties) = preSolution 50 | HideSolutionNode = FALSE 51 | EndGlobalSection 52 | GlobalSection(ExtensibilityGlobals) = postSolution 53 | SolutionGuid = {5238B2E8-5295-47DF-B781-F6760E906214} 54 | EndGlobalSection 55 | EndGlobal 56 | -------------------------------------------------------------------------------- /src/Majorsilence.Media.Web/Controllers/ConvertController.cs: -------------------------------------------------------------------------------- 1 | using Asp.Versioning; 2 | using Microsoft.AspNetCore.Mvc; 3 | using UUIDNext; 4 | 5 | namespace Majorsilence.Media.Web.Controllers; 6 | 7 | [Route("api/v{version:apiVersion}/[controller]")] 8 | [ApiController] 9 | [ApiVersion("1.0")] 10 | public class ConvertController : ControllerBase 11 | { 12 | private readonly ILogger _logger; 13 | private readonly Settings _settings; 14 | 15 | public ConvertController(ILogger logger, 16 | Settings settings) 17 | { 18 | _logger = logger; 19 | _settings = settings; 20 | } 21 | 22 | /// 23 | /// Initiate a new file id. This most be called before uploading a file. The returned value 24 | /// must be sent in the Post method as a header with the name "FileId". 25 | /// 26 | /// 27 | public string Get() 28 | { 29 | // The purpose of this method is to permit the client to get a file id 30 | // to track the file upload. This is to prevent a client from uploading 31 | // a file and then not sending the file to be processed. This method 32 | // will create a file in the upload folder with the file id as the name 33 | // and the contents will be the token. The client must send the token 34 | // in the header of the Post method. The token is the bearer token 35 | // from the client. 36 | // The WorkerService will then process the file and delete the file 37 | // after processing. If the file is not processed then the file will 38 | // remain in the upload folder and the client can try again. 39 | var id = Uuid.NewDatabaseFriendly(Database.Other).ToString(); 40 | var startRequestPath = Path.Combine(_settings.UploadFolder, $"{id}.startrequest"); 41 | System.IO.File.WriteAllText(startRequestPath, GetBearerToken()); 42 | return id; 43 | } 44 | 45 | /// 46 | /// Upload a file and return its id. 47 | /// 48 | /// 49 | [HttpPost] 50 | [RequestSizeLimit(107374182400)] // 100 GB 51 | [RequestFormLimits(MultipartBodyLengthLimit = 107374182400)] // 100 GB 52 | public async Task Post(IFormFile file) 53 | { 54 | if (file.Length <= 0) return BadRequest("File is empty"); 55 | 56 | var fileid = Request.Headers["FileId"].ToString(); 57 | if (string.IsNullOrWhiteSpace(fileid)) 58 | { 59 | return BadRequest("The file id must be sent in the header with the name 'FileId'"); 60 | } 61 | 62 | if (fileid.Trim().Length != 36) 63 | { 64 | return BadRequest("Invalid file id. Must be retrieved by calling GET."); 65 | } 66 | 67 | var token = GetBearerToken(); 68 | var startRequestPath = Path.Combine(_settings.UploadFolder, $"{fileid}.startrequest"); 69 | if (!System.IO.File.Exists(startRequestPath)) 70 | { 71 | return BadRequest("Invalid file id. Must be retrieved by calling Get."); 72 | } 73 | string startRequestToken = System.IO.File.ReadAllText(startRequestPath); 74 | if (!string.Equals(startRequestToken, token)) 75 | { 76 | return BadRequest("Invalid token. Tokens must match between GET and POST requests."); 77 | } 78 | 79 | var ext = Path.GetExtension(file.FileName); 80 | await using var inputStream = new FileStream(Path.Combine(_settings.UploadFolder, 81 | $"{fileid}"), 82 | FileMode.Create); 83 | 84 | var uploadDetailFilePath = Path.Combine(_settings.UploadFolder, $"{fileid}.txt"); 85 | await file.CopyToAsync(inputStream); 86 | await System.IO.File.WriteAllTextAsync(uploadDetailFilePath, $"{fileid}{Environment.NewLine}{ext}"); 87 | 88 | return Ok(fileid); 89 | } 90 | 91 | private string GetBearerToken() 92 | { 93 | string authorizationHeader = Request.Headers["Authorization"]; 94 | string[] parts = authorizationHeader.Split(' '); 95 | string scheme = parts[0]; 96 | string token = parts[1]; 97 | return token; 98 | } 99 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Web/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base 4 | WORKDIR /app 5 | EXPOSE 8080 6 | EXPOSE 8443 7 | 8 | FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build 9 | WORKDIR /src 10 | COPY ["Majorsilence.Media.Web/Majorsilence.Media.Web.csproj", "Majorsilence.Media.Web/"] 11 | COPY ["Majorsilence.Media.Videos/Majorsilence.Media.Videos.csproj", "Majorsilence.Media.Videos/"] 12 | COPY ["Majorsilence.Media.Images/Majorsilence.Media.Images.csproj", "Majorsilence.Media.Images/"] 13 | RUN dotnet restore "Majorsilence.Media.Web/Majorsilence.Media.Web.csproj" 14 | COPY . . 15 | WORKDIR "/src/Majorsilence.Media.Web" 16 | RUN dotnet build "Majorsilence.Media.Web.csproj" -c Release -o /app/build 17 | 18 | FROM build AS publish 19 | RUN dotnet publish "Majorsilence.Media.Web.csproj" -c Release -o /app/publish /p:UseAppHost=false 20 | 21 | FROM base AS final 22 | WORKDIR /app 23 | COPY --from=publish /app/publish . 24 | # USER app is the default starting with aspnet:8.0 images 25 | # See https://devblogs.microsoft.com/dotnet/running-nonroot-kubernetes-with-dotnet/ and 26 | # https://devblogs.microsoft.com/dotnet/securing-containers-with-rootless/ 27 | USER $APP_UID 28 | ENTRYPOINT ["dotnet", "Majorsilence.Media.Web.dll"] -------------------------------------------------------------------------------- /src/Majorsilence.Media.Web/JwtSecrets.cs: -------------------------------------------------------------------------------- 1 | namespace Majorsilence.Media.Web; 2 | 3 | public class JwtSettings 4 | { 5 | public string Audience { get; init; } 6 | public string Issuer { get; init; } 7 | public string Secret { get; init; } 8 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Web/Majorsilence.Media.Web.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | 261ca7b9-2ec9-4aad-953d-4c84831f78f1 8 | Linux 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Majorsilence.Media.Web/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Asp.Versioning; 3 | using Majorsilence.Media.Web; 4 | using Microsoft.AspNetCore.Authentication.JwtBearer; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.IdentityModel.Tokens; 7 | using Microsoft.OpenApi.Models; 8 | 9 | var builder = WebApplication.CreateBuilder(args); 10 | builder.Services.AddScoped(s => { return builder.Configuration.GetSection("ApiSettings").Get(); }); 11 | 12 | builder.Services.AddCors(options => 13 | { 14 | options.AddPolicy("AllowSpecificOrigin", 15 | b => b.WithOrigins(builder.Configuration.GetSection("ApiSettings:PermittedCORS").Get()) 16 | .AllowAnyHeader() 17 | .AllowAnyMethod()); 18 | }); 19 | builder.Services.AddControllers(); 20 | builder.Services.AddApiVersioning(config => 21 | { 22 | config.DefaultApiVersion = new ApiVersion(1, 0); 23 | config.AssumeDefaultVersionWhenUnspecified = true; 24 | config.ReportApiVersions = true; 25 | }); 26 | // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle 27 | builder.Services.AddEndpointsApiExplorer(); 28 | builder.Services.AddSwaggerGen(c => 29 | { 30 | c.SwaggerDoc("v1", new OpenApiInfo { Title = "Majorsilence.Media.Web", Version = "v1" }); 31 | }); 32 | 33 | var symmetricSecurityKey = 34 | new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["ApiSettings:Jwt:Secret"])); 35 | builder.Services.AddAuthentication(options => { options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; }) 36 | .AddJwtBearer(options => 37 | { 38 | options.SaveToken = true; 39 | options.TokenValidationParameters = new TokenValidationParameters() 40 | { 41 | ValidIssuer = builder.Configuration["ApiSettings:Jwt:Issuer"], 42 | ValidAudience = builder.Configuration["ApiSettings:Jwt:Audience"], 43 | IssuerSigningKey = symmetricSecurityKey 44 | }; 45 | }); 46 | 47 | builder.Services.AddAuthorization(options => 48 | { 49 | options.AddPolicy("FileUpload", policy => 50 | { 51 | policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme); 52 | policy.RequireAuthenticatedUser(); 53 | }); 54 | options.DefaultPolicy = options.GetPolicy("FileUpload"); 55 | options.FallbackPolicy = options.DefaultPolicy; 56 | }); 57 | builder.Services.AddHealthChecks(); 58 | 59 | var app = builder.Build(); 60 | 61 | // Configure the HTTP request pipeline. 62 | if (app.Environment.IsDevelopment()) 63 | { 64 | app.UseSwagger(); 65 | app.UseSwaggerUI(); 66 | } 67 | app.UseCors("AllowSpecificOrigin"); 68 | app.UseHttpsRedirection(); 69 | 70 | app.UseAuthentication(); 71 | app.UseAuthorization(); 72 | 73 | app.MapControllers(); 74 | app.MapHealthChecks("/healthz").AllowAnonymous(); 75 | app.Run(); -------------------------------------------------------------------------------- /src/Majorsilence.Media.Web/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Majorsilence.Media.Web": { 4 | "commandName": "Project", 5 | "launchBrowser": true, 6 | "launchUrl": "swagger", 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development" 9 | }, 10 | "dotnetRunMessages": true, 11 | "applicationUrl": "https://localhost:7079;http://localhost:5079" 12 | }, 13 | "IIS Express": { 14 | "commandName": "IISExpress", 15 | "launchBrowser": true, 16 | "launchUrl": "swagger", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "Docker": { 22 | "commandName": "Docker", 23 | "launchBrowser": true, 24 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", 25 | "publishAllPorts": true, 26 | "useSSL": true 27 | } 28 | }, 29 | "$schema": "https://json.schemastore.org/launchsettings.json", 30 | "iisSettings": { 31 | "windowsAuthentication": false, 32 | "anonymousAuthentication": true, 33 | "iisExpress": { 34 | "applicationUrl": "http://localhost:18143", 35 | "sslPort": 44326 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Web/Settings.cs: -------------------------------------------------------------------------------- 1 | namespace Majorsilence.Media.Web; 2 | 3 | public class Settings 4 | { 5 | public string UploadFolder { get; init; } 6 | public JwtSettings Jwt { get; init; } 7 | public string[] PermittedCORSOrigins { get; init; } 8 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.Web/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "ApiSettings": { 9 | "UploadFolder": "/home/peter/Downloads/web/uploads", 10 | "PermittedCORS": [ 11 | "https://*.majorsilence.com", 12 | "https://localhost:7090", 13 | "http://localhost:5142", 14 | "http://*.majorsilence.local" 15 | ], 16 | "Jwt": { 17 | "Secret": "PLACEHOLDER", 18 | "Issuer": "PLACEHOLDER", 19 | "Audience": "PLACEHOLDER" 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Majorsilence.Media.Web/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*", 9 | "ApiSettings": { 10 | "UploadFolder": "PLACEHOLDER", 11 | "PermittedCORS": [ 12 | "https://*.majorsilence.com" 13 | ], 14 | "Jwt":{ 15 | "Secret": "PLACEHOLDER", 16 | "Issuer": "PLACEHOLDER", 17 | "Audience": "PLACEHOLDER" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Majorsilence.Media.WorkerService.Tests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using NUnit.Framework; -------------------------------------------------------------------------------- /src/Majorsilence.Media.WorkerService.Tests/Majorsilence.Media.WorkerService.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | PreserveNewest 32 | 33 | 34 | 35 | PreserveNewest 36 | 37 | 38 | 39 | PreserveNewest 40 | 41 | 42 | 43 | PreserveNewest 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/Majorsilence.Media.WorkerService/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/runtime:9.0 AS base 4 | WORKDIR /app 5 | 6 | FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build 7 | WORKDIR /src 8 | COPY ["Majorsilence.Media.WorkerService/Majorsilence.Media.WorkerService.csproj", "Majorsilence.Media.WorkerService/"] 9 | RUN dotnet restore "Majorsilence.Media.WorkerService/Majorsilence.Media.WorkerService.csproj" 10 | COPY . . 11 | WORKDIR "/src/Majorsilence.Media.WorkerService" 12 | RUN dotnet build "Majorsilence.Media.WorkerService.csproj" -c Release -o /app/build 13 | 14 | FROM build AS publish 15 | RUN dotnet publish "Majorsilence.Media.WorkerService.csproj" -c Release -o /app/publish /p:UseAppHost=false 16 | 17 | FROM base AS final 18 | WORKDIR /app 19 | COPY --from=publish /app/publish . 20 | # USER app is the default starting with aspnet:8.0 images 21 | # See https://devblogs.microsoft.com/dotnet/running-nonroot-kubernetes-with-dotnet/ and 22 | # https://devblogs.microsoft.com/dotnet/securing-containers-with-rootless/ 23 | USER $APP_UID 24 | ENTRYPOINT ["dotnet", "Majorsilence.Media.WorkerService.dll"] -------------------------------------------------------------------------------- /src/Majorsilence.Media.WorkerService/Majorsilence.Media.WorkerService.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | dotnet-Majorsilence.Media.WorkerService-52B482C9-8D3F-4035-B060-C4C8FD36EE67 8 | Linux 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Majorsilence.Media.WorkerService/Program.cs: -------------------------------------------------------------------------------- 1 | using Majorsilence.Media.Videos; 2 | using Majorsilence.Media.WorkerService; 3 | 4 | var host = Host.CreateDefaultBuilder(args) 5 | .ConfigureServices(services => 6 | { 7 | services.AddSingleton(s => 8 | { 9 | return s.GetService() 10 | .GetSection("ApiSettings") 11 | .Get(); 12 | }); 13 | 14 | services.AddSingleton(s => { return new Ffmpeg(s.GetService().FfmpegPath); }); 15 | 16 | services.AddHostedService(); 17 | }) 18 | .Build(); 19 | 20 | await host.RunAsync(); -------------------------------------------------------------------------------- /src/Majorsilence.Media.WorkerService/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Majorsilence.Media.WorkerService": { 4 | "commandName": "Project", 5 | "environmentVariables": { 6 | "DOTNET_ENVIRONMENT": "Development" 7 | }, 8 | "dotnetRunMessages": true 9 | }, 10 | "Docker": { 11 | "commandName": "Docker" 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.WorkerService/Settings.cs: -------------------------------------------------------------------------------- 1 | using Majorsilence.Media.Videos; 2 | 3 | namespace Majorsilence.Media.WorkerService; 4 | 5 | public class Settings 6 | { 7 | public string UploadFolder { get; init; } 8 | public string MEncoderPath { get; init; } 9 | public string ConvertedFolder { get; init; } 10 | public string FfmpegPath { get; init; } 11 | /// 12 | /// Valid ConversionType values are "streaming", "download", "all" 13 | /// 14 | public string ConversionType { get; init; } 15 | public VideoAspectRatios[] AspectRatios { get; init; } 16 | public Dictionary VideoAudioConverters { get; init; } 17 | public Dictionary VideoFileExtension { get; init; } 18 | public StreamTypes StreamTypes { get; init; } 19 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.WorkerService/StreamTypes.cs: -------------------------------------------------------------------------------- 1 | namespace Majorsilence.Media.WorkerService; 2 | 3 | public class StreamTypes 4 | { 5 | public string MpegDash { get; init; } 6 | public string Hls { get; init; } 7 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.WorkerService/Worker.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using Majorsilence.Media.Videos; 3 | 4 | namespace Majorsilence.Media.WorkerService; 5 | 6 | public class Worker : BackgroundService 7 | { 8 | private readonly ILogger _logger; 9 | private readonly Settings _settings; 10 | private readonly IVideoEncoder _videoEncoder; 11 | 12 | public Worker(ILogger logger, IVideoEncoder videoEncoder, Settings settings) 13 | { 14 | _logger = logger; 15 | _videoEncoder = videoEncoder; 16 | _settings = settings; 17 | } 18 | 19 | protected override async Task ExecuteAsync(CancellationToken stoppingToken) 20 | { 21 | int countNothingToDo = 0; 22 | while (!stoppingToken.IsCancellationRequested) 23 | { 24 | _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); 25 | string extraConvertedSubFolder = System.IO.Path.Combine(_settings.ConvertedFolder, 26 | DateTime.UtcNow.Year.ToString(), 27 | DateTime.UtcNow.Month.ToString(), DateTime.UtcNow.Day.ToString()); 28 | var x = new TranscodingManager(_videoEncoder, _settings); 29 | int transcodingCount = await x.Transcode(extraConvertedSubFolder, stoppingToken); 30 | if (transcodingCount == 0) 31 | { 32 | countNothingToDo++; 33 | if (countNothingToDo > 60) 34 | { 35 | await Task.Delay(60000, stoppingToken); 36 | continue; 37 | } 38 | 39 | await Task.Delay(1000 * countNothingToDo, stoppingToken); 40 | } 41 | else 42 | { 43 | countNothingToDo = 0; 44 | } 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/Majorsilence.Media.WorkerService/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.Hosting.Lifetime": "Information" 6 | } 7 | }, 8 | "ApiSettings": { 9 | "UploadFolder": "/home/peter/Downloads/web/uploads", 10 | "ConvertedFolder": "/home/peter/Downloads/web/output", 11 | "MEncoderPath": "/usr/bin/mencoder", 12 | "FfmpegPath": "/usr/bin/ffmpeg", 13 | "AspectRatios": [ 14 | "original", 15 | "p720", 16 | "p480", 17 | "p360", 18 | "p240" 19 | ], 20 | "VideoAudioConverters": { 21 | "vp9": "opus", 22 | "x264": "aac", 23 | "x265": "aac" 24 | }, 25 | "VideoFileExtension": { 26 | "vp9": "webm", 27 | "x264": "mp4", 28 | "x265": "mp4" 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Majorsilence.Media.WorkerService/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.Hosting.Lifetime": "Information" 6 | } 7 | }, 8 | "ApiSettings": { 9 | "UploadFolder": "PLACEHOLDER, should match upload folder in Majorsilence.Media.Web", 10 | "ConvertedFolder": "PLACEHOLDER, location that converted videos should be saved", 11 | "MEncoderPath": "mencoder", 12 | "FfmpegPath": "/usr/bin/ffmpeg", 13 | // Valid ConversionTypes are "streaming", "download", or "all" 14 | "ConversionType": "streaming", 15 | "AspectRatios": [ 16 | "original", 17 | //"p7680", 18 | //"p2160", 19 | //"p1440,", 20 | "p1080", 21 | "p720", 22 | "p480", 23 | "p360", 24 | "p240" 25 | ], 26 | "VideoAudioConverters": { 27 | //"av1": "opus", 28 | "vp9": "opus", 29 | "x264": "aac" 30 | //"x265": "aac" 31 | }, 32 | "VideoFileExtension": { 33 | "av1": "mkv", 34 | "vp9": "webm", 35 | "x264": "mp4", 36 | "x265": "mp4" 37 | }, 38 | "StreamingTypes": { 39 | "MpegDash": "-i [PLACEHOLDER_INPUT] -map 0:v -map 0:a -s:v:0 426x240 -c:v:0 libx264 -b:v:0 250k -s:v:1 640x360 -c:v:1 libx264 -b:v:1 800k -s:v:2 854x480 -c:v:2 libx264 -b:v:2 1400k -s:v:3 1280x720 -c:v:3 libx264 -b:v:3 2800k -s:v:4 1920x1080 -c:v:4 libx264 -b:v:4 5000k -s:v:5 3840x2160 -c:v:5 libx264 -b:v:5 14000k -s:v:6 426x240 -c:v:6 libvpx-vp9 -b:v:6 250k -s:v:7 640x360 -c:v:7 libvpx-vp9 -b:v:7 800k -s:v:8 854x480 -c:v:8 libvpx-vp9 -b:v:8 1400k -s:v:9 1280x720 -c:v:9 libvpx-vp9 -b:v:9 2800k -s:v:10 1920x1080 -c:v:10 libvpx-vp9 -b:v:10 5000k -s:v:11 3840x2160 -c:v:11 libvpx-vp9 -b:v:11 14000k -c:a aac -b:a 128k -var_stream_map \"v:0,a:0 v:1,a:0 v:2,a:0 v:3,a:0 v:4,a:0 v:5,a:0 v:6,a:0 v:7,a:0 v:8,a:0 v:9,a:0 v:10,a:0 v:11,a:0\" -f dash -use_template 1 -use_timeline 1 -adaptation_sets \"id=0,streams=v id=1,streams=a\" [PLACEHOLDER_OUTPUT].mpd", 40 | "Hls": "-i [PLACEHOLDER_INPUT] -map 0:v -map 0:a -s:v:0 426x240 -c:v:0 libx264 -b:v:0 250k -s:v:1 640x360 -c:v:1 libx264 -b:v:1 800k -s:v:2 854x480 -c:v:2 libx264 -b:v:2 1400k -s:v:3 1280x720 -c:v:3 libx264 -b:v:3 2800k -s:v:4 1920x1080 -c:v:4 libx264 -b:v:4 5000k -s:v:5 3840x2160 -c:v:5 libx264 -b:v:5 14000k -c:a aac -b:a 128k -hls_time 4 -hls_playlist_type vod -hls_segment_filename [PLACEHOLDER_OUTPUT]_%03d.ts [PLACEHOLDER_OUTPUT].m3u8" 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Majorsilence.Winforms.MPlayerControl.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.2.32630.192 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Majorsilence.Media.Videos", "Majorsilence.Media.Videos\Majorsilence.Media.Videos.csproj", "{5D3616D0-160D-4601-AB67-E1ED9111C676}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Majorsilence.Media.NunitTests", "Majorsilence.Media.NunitTests\Majorsilence.Media.NunitTests.csproj", "{F8A7EE08-6BEA-4CB4-A0BA-22169133289A}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaPlayer", "MediaPlayer\MediaPlayer.csproj", "{AFF738E9-1179-4E9D-A803-1CA5CB5499D1}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SlideShow", "SlideShow\SlideShow.csproj", "{6B5C1B79-1CDF-409C-8CAF-B3D2F25E27B5}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Majorsilence.Media.Images", "Majorsilence.Media.Images\Majorsilence.Media.Images.csproj", "{C859FA27-7882-4814-9202-9C9D807C2CE5}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibMPlayerWinform", "LibMPlayerWinform\LibMPlayerWinform.csproj", "{994F562F-B694-4799-B1D6-C90A290FEF2B}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "Test\Test.csproj", "{FAF8DCB0-403C-4120-A70C-89DD942564A9}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Majorsilence.Media.Desktop.UI", "Majorsilence.Media.Desktop.UI\Majorsilence.Media.Desktop.UI.csproj", "{E8077661-DF84-4373-8D69-32F1684EEFA3}" 21 | EndProject 22 | Global 23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 24 | Debug|Any CPU = Debug|Any CPU 25 | Release|Any CPU = Release|Any CPU 26 | EndGlobalSection 27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 28 | {5D3616D0-160D-4601-AB67-E1ED9111C676}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {5D3616D0-160D-4601-AB67-E1ED9111C676}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {5D3616D0-160D-4601-AB67-E1ED9111C676}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {5D3616D0-160D-4601-AB67-E1ED9111C676}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {F8A7EE08-6BEA-4CB4-A0BA-22169133289A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {F8A7EE08-6BEA-4CB4-A0BA-22169133289A}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {F8A7EE08-6BEA-4CB4-A0BA-22169133289A}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {F8A7EE08-6BEA-4CB4-A0BA-22169133289A}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {AFF738E9-1179-4E9D-A803-1CA5CB5499D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {AFF738E9-1179-4E9D-A803-1CA5CB5499D1}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {AFF738E9-1179-4E9D-A803-1CA5CB5499D1}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {AFF738E9-1179-4E9D-A803-1CA5CB5499D1}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {6B5C1B79-1CDF-409C-8CAF-B3D2F25E27B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {6B5C1B79-1CDF-409C-8CAF-B3D2F25E27B5}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {6B5C1B79-1CDF-409C-8CAF-B3D2F25E27B5}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {6B5C1B79-1CDF-409C-8CAF-B3D2F25E27B5}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {C859FA27-7882-4814-9202-9C9D807C2CE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {C859FA27-7882-4814-9202-9C9D807C2CE5}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {C859FA27-7882-4814-9202-9C9D807C2CE5}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {C859FA27-7882-4814-9202-9C9D807C2CE5}.Release|Any CPU.Build.0 = Release|Any CPU 48 | {994F562F-B694-4799-B1D6-C90A290FEF2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {994F562F-B694-4799-B1D6-C90A290FEF2B}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {994F562F-B694-4799-B1D6-C90A290FEF2B}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {994F562F-B694-4799-B1D6-C90A290FEF2B}.Release|Any CPU.Build.0 = Release|Any CPU 52 | {FAF8DCB0-403C-4120-A70C-89DD942564A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 53 | {FAF8DCB0-403C-4120-A70C-89DD942564A9}.Debug|Any CPU.Build.0 = Debug|Any CPU 54 | {FAF8DCB0-403C-4120-A70C-89DD942564A9}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {FAF8DCB0-403C-4120-A70C-89DD942564A9}.Release|Any CPU.Build.0 = Release|Any CPU 56 | {E8077661-DF84-4373-8D69-32F1684EEFA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 57 | {E8077661-DF84-4373-8D69-32F1684EEFA3}.Debug|Any CPU.Build.0 = Debug|Any CPU 58 | {E8077661-DF84-4373-8D69-32F1684EEFA3}.Release|Any CPU.ActiveCfg = Release|Any CPU 59 | {E8077661-DF84-4373-8D69-32F1684EEFA3}.Release|Any CPU.Build.0 = Release|Any CPU 60 | EndGlobalSection 61 | GlobalSection(SolutionProperties) = preSolution 62 | HideSolutionNode = FALSE 63 | EndGlobalSection 64 | GlobalSection(ExtensibilityGlobals) = postSolution 65 | SolutionGuid = {581875EC-8089-45E4-853B-789F5E7991E9} 66 | EndGlobalSection 67 | GlobalSection(MonoDevelopProperties) = preSolution 68 | StartupItem = MediaPlayer\MediaPlayer.csproj 69 | EndGlobalSection 70 | EndGlobal 71 | -------------------------------------------------------------------------------- /src/MediaPlayer/MediaPlayer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | WinExe 4 | net6.0-windows;net8.0-windows;net9.0-windows 5 | enable 6 | true 7 | enable 8 | MediaPlayer.Program 9 | MediaPlayer 10 | 11 | 12 | bin\$(Configuration)\ 13 | Full 14 | True 15 | 16 | 17 | bin\$(Configuration)\ 18 | None 19 | 20 | 21 | -path "/usr/bin/mplayer" 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -path "/usr/bin/mplayer" 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | Form 39 | 40 | 41 | PlayerProperties.cs 42 | 43 | 44 | Form 45 | 46 | 47 | Player.cs 48 | 49 | 50 | True 51 | True 52 | Resources.resx 53 | 54 | 55 | True 56 | True 57 | Settings.settings 58 | 59 | 60 | 61 | 62 | Player.cs 63 | 64 | 65 | PlayerProperties.cs 66 | 67 | 68 | ResXFileCodeGenerator 69 | Resources.Designer.cs 70 | Designer 71 | 72 | 73 | 74 | 75 | SettingsSingleFileGenerator 76 | Settings.Designer.cs 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /src/MediaPlayer/PlayerProperties.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace MediaPlayer 2 | { 3 | partial class PlayerProperties 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.label1 = new System.Windows.Forms.Label(); 32 | this.textBox1 = new System.Windows.Forms.TextBox(); 33 | this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog(); 34 | this.btnMPlayerPath = new System.Windows.Forms.Button(); 35 | this.statusStrip1 = new System.Windows.Forms.StatusStrip(); 36 | this.lblStatus = new System.Windows.Forms.ToolStripStatusLabel(); 37 | this.btnSave = new System.Windows.Forms.Button(); 38 | this.statusStrip1.SuspendLayout(); 39 | this.SuspendLayout(); 40 | // 41 | // label1 42 | // 43 | this.label1.AutoSize = true; 44 | this.label1.Location = new System.Drawing.Point(14, 15); 45 | this.label1.Name = "label1"; 46 | this.label1.Size = new System.Drawing.Size(73, 13); 47 | this.label1.TabIndex = 0; 48 | this.label1.Text = "MPlayer/libmpv Path:"; 49 | // 50 | // textBox1 51 | // 52 | this.textBox1.Location = new System.Drawing.Point(93, 12); 53 | this.textBox1.Name = "textBox1"; 54 | this.textBox1.Size = new System.Drawing.Size(403, 20); 55 | this.textBox1.TabIndex = 1; 56 | // 57 | // openFileDialog1 58 | // 59 | this.openFileDialog1.FileName = "openFileDialog1"; 60 | // 61 | // btnMPlayerPath 62 | // 63 | this.btnMPlayerPath.Location = new System.Drawing.Point(504, 9); 64 | this.btnMPlayerPath.Name = "btnMPlayerPath"; 65 | this.btnMPlayerPath.Size = new System.Drawing.Size(114, 23); 66 | this.btnMPlayerPath.TabIndex = 2; 67 | this.btnMPlayerPath.Text = "Select MPlayer/libmpv Path"; 68 | this.btnMPlayerPath.UseVisualStyleBackColor = true; 69 | this.btnMPlayerPath.Click += new System.EventHandler(this.btnMPlayerPath_Click); 70 | // 71 | // statusStrip1 72 | // 73 | this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] 74 | { 75 | this.lblStatus 76 | }); 77 | this.statusStrip1.Location = new System.Drawing.Point(0, 106); 78 | this.statusStrip1.Name = "statusStrip1"; 79 | this.statusStrip1.Size = new System.Drawing.Size(630, 22); 80 | this.statusStrip1.TabIndex = 3; 81 | this.statusStrip1.Text = "statusStrip1"; 82 | // 83 | // lblStatus 84 | // 85 | this.lblStatus.Name = "lblStatus"; 86 | this.lblStatus.Size = new System.Drawing.Size(0, 17); 87 | // 88 | // btnSave 89 | // 90 | this.btnSave.Location = new System.Drawing.Point(504, 80); 91 | this.btnSave.Name = "btnSave"; 92 | this.btnSave.Size = new System.Drawing.Size(114, 23); 93 | this.btnSave.TabIndex = 4; 94 | this.btnSave.Text = "Save"; 95 | this.btnSave.UseVisualStyleBackColor = true; 96 | this.btnSave.Click += new System.EventHandler(this.btnSave_Click); 97 | // 98 | // PlayerProperties 99 | // 100 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 101 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 102 | this.ClientSize = new System.Drawing.Size(630, 128); 103 | this.Controls.Add(this.btnSave); 104 | this.Controls.Add(this.statusStrip1); 105 | this.Controls.Add(this.btnMPlayerPath); 106 | this.Controls.Add(this.textBox1); 107 | this.Controls.Add(this.label1); 108 | this.Name = "PlayerProperties"; 109 | this.Text = "Player Properties"; 110 | this.Load += new System.EventHandler(this.PlayerProperties_Load); 111 | this.statusStrip1.ResumeLayout(false); 112 | this.statusStrip1.PerformLayout(); 113 | this.ResumeLayout(false); 114 | this.PerformLayout(); 115 | 116 | } 117 | 118 | #endregion 119 | 120 | private System.Windows.Forms.Label label1; 121 | private System.Windows.Forms.TextBox textBox1; 122 | private System.Windows.Forms.OpenFileDialog openFileDialog1; 123 | private System.Windows.Forms.Button btnMPlayerPath; 124 | private System.Windows.Forms.StatusStrip statusStrip1; 125 | private System.Windows.Forms.ToolStripStatusLabel lblStatus; 126 | private System.Windows.Forms.Button btnSave; 127 | } 128 | } -------------------------------------------------------------------------------- /src/MediaPlayer/PlayerProperties.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012 (C) Peter Gill 3 | 4 | This file is part of MediaPlayer. 5 | 6 | MediaPlayer is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | MediaPlayer is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.ComponentModel; 23 | using System.Data; 24 | using System.Drawing; 25 | using System.Linq; 26 | using System.Text; 27 | using System.Windows.Forms; 28 | 29 | namespace MediaPlayer 30 | { 31 | public partial class PlayerProperties : Form 32 | { 33 | public PlayerProperties() 34 | { 35 | InitializeComponent(); 36 | } 37 | 38 | private void PlayerProperties_Load(object sender, EventArgs e) 39 | { 40 | textBox1.Text = MediaPlayer.Properties.Settings.Default.MPlayerPath; 41 | } 42 | 43 | private void btnMPlayerPath_Click(object sender, EventArgs e) 44 | { 45 | if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK) 46 | { 47 | textBox1.Text = openFileDialog1.FileName; 48 | } 49 | } 50 | 51 | private void btnSave_Click(object sender, EventArgs e) 52 | { 53 | MediaPlayer.Properties.Settings.Default.MPlayerPath = textBox1.Text.Trim(); 54 | MediaPlayer.Properties.Settings.Default.Save(); 55 | } 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/MediaPlayer/Program.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2010 (C) Peter Gill 4 | 5 | This file is part of MediaPlayer. 6 | 7 | MediaPlayer is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | MediaPlayer is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | 23 | using System; 24 | using System.Windows.Forms; 25 | 26 | namespace MediaPlayer 27 | { 28 | /// 29 | /// Class with program entry point. 30 | /// 31 | internal sealed class Program 32 | { 33 | /// 34 | /// Program entry point. 35 | /// 36 | [STAThread] 37 | private static void Main(string[] args) 38 | { 39 | 40 | string path = ""; 41 | for (int i = 0; i <= Environment.GetCommandLineArgs().Length - 1; i++) 42 | { 43 | if (Environment.GetCommandLineArgs()[i] == "-path") 44 | { 45 | path = Environment.GetCommandLineArgs()[i + 1].Trim(); 46 | } 47 | } 48 | 49 | if (!string.IsNullOrWhiteSpace(path)) 50 | { 51 | MediaPlayer.Properties.Settings.Default.MPlayerPath = path; 52 | MediaPlayer.Properties.Settings.Default.Save(); 53 | } 54 | 55 | Application.EnableVisualStyles(); 56 | Application.SetCompatibleTextRenderingDefault(false); 57 | Application.Run(new Player("", false, false)); 58 | } 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/MediaPlayer/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.1 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace MediaPlayer.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | 26 | [global::System.Configuration.UserScopedSettingAttribute()] 27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 28 | [global::System.Configuration.DefaultSettingValueAttribute("")] 29 | public string MPlayerPath { 30 | get { 31 | return ((string)(this["MPlayerPath"])); 32 | } 33 | set { 34 | this["MPlayerPath"] = value; 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/MediaPlayer/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/MediaPlayer/Resources/audio-volume-medium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/src/MediaPlayer/Resources/audio-volume-medium.png -------------------------------------------------------------------------------- /src/MediaPlayer/Resources/audio-volume-muted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/src/MediaPlayer/Resources/audio-volume-muted.png -------------------------------------------------------------------------------- /src/MediaPlayer/Resources/config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/src/MediaPlayer/Resources/config.png -------------------------------------------------------------------------------- /src/MediaPlayer/Resources/document-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/src/MediaPlayer/Resources/document-open.png -------------------------------------------------------------------------------- /src/MediaPlayer/Resources/fastforward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/src/MediaPlayer/Resources/fastforward.png -------------------------------------------------------------------------------- /src/MediaPlayer/Resources/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/src/MediaPlayer/Resources/pause.png -------------------------------------------------------------------------------- /src/MediaPlayer/Resources/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/src/MediaPlayer/Resources/play.png -------------------------------------------------------------------------------- /src/MediaPlayer/Resources/rewind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/src/MediaPlayer/Resources/rewind.png -------------------------------------------------------------------------------- /src/MediaPlayer/Resources/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/src/MediaPlayer/Resources/stop.png -------------------------------------------------------------------------------- /src/MediaPlayer/Resources/volume-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/majorsilence/MPlayerControl/ce94a55853a4f16a89e9090a1dacddcb03851324/src/MediaPlayer/Resources/volume-down.png -------------------------------------------------------------------------------- /src/MediaPlayer/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/SlideShow/Program.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2011 (C) Peter Gill 4 | 5 | This file is part of SlideShow. 6 | 7 | SlideShow is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | SlideShow is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | 20 | */ 21 | 22 | using System; 23 | using System.Collections.Generic; 24 | using System.Linq; 25 | using System.Windows.Forms; 26 | 27 | namespace SlideShow 28 | { 29 | static class Program 30 | { 31 | /// 32 | /// The main entry point for the application. 33 | /// 34 | [STAThread] 35 | static void Main() 36 | { 37 | Application.EnableVisualStyles(); 38 | Application.SetCompatibleTextRenderingDefault(false); 39 | Application.Run(new MainForm()); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/SlideShow/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace SlideShow.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SlideShow.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/SlideShow/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /src/SlideShow/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.235 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace SlideShow.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/SlideShow/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/SlideShow/SlideShow.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | x86 4 | WinExe 5 | net6.0-windows;net8.0-windows;net9.0-windows 6 | enable 7 | true 8 | enable 9 | SlideShow.Program 10 | SlideShow 11 | 12 | 13 | full 14 | 15 | 16 | pdbonly 17 | 18 | 19 | full 20 | bin\Debug\SlideShow.exe.CodeAnalysisLog.xml 21 | true 22 | GlobalSuppressions.cs 23 | MinimumRecommendedRules.ruleset 24 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 25 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 26 | false 27 | 28 | 29 | pdbonly 30 | bin\Release\SlideShow.exe.CodeAnalysisLog.xml 31 | true 32 | GlobalSuppressions.cs 33 | MinimumRecommendedRules.ruleset 34 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 35 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 36 | false 37 | 38 | 39 | 40 | Form 41 | 42 | 43 | MainForm.cs 44 | 45 | 46 | MainForm.cs 47 | 48 | 49 | ResXFileCodeGenerator 50 | Resources.Designer.cs 51 | Designer 52 | 53 | 54 | True 55 | Resources.resx 56 | True 57 | 58 | 59 | SettingsSingleFileGenerator 60 | Settings.Designer.cs 61 | 62 | 63 | True 64 | Settings.settings 65 | True 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/SlideShow/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/Test/Form1.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace Test 2 | { 3 | partial class Form1 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.winFormMPlayerControl1 = new LibMPlayerWinform.WinFormMPlayerControl(); 32 | this.SuspendLayout(); 33 | // 34 | // winFormMPlayerControl1 35 | // 36 | this.winFormMPlayerControl1.Dock = System.Windows.Forms.DockStyle.Fill; 37 | this.winFormMPlayerControl1.Location = new System.Drawing.Point(0, 0); 38 | this.winFormMPlayerControl1.MPlayerPath = null; 39 | this.winFormMPlayerControl1.Name = "winFormMPlayerControl1"; 40 | this.winFormMPlayerControl1.Size = new System.Drawing.Size(944, 572); 41 | this.winFormMPlayerControl1.TabIndex = 0; 42 | this.winFormMPlayerControl1.VideoPath = null; 43 | this.winFormMPlayerControl1.Load += new System.EventHandler(this.winFormMPlayerControl1_Load); 44 | // 45 | // Form1 46 | // 47 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 48 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 49 | this.ClientSize = new System.Drawing.Size(944, 572); 50 | this.Controls.Add(this.winFormMPlayerControl1); 51 | this.Name = "Form1"; 52 | this.Text = "Form1"; 53 | this.ResumeLayout(false); 54 | 55 | } 56 | 57 | #endregion 58 | 59 | private LibMPlayerWinform.WinFormMPlayerControl winFormMPlayerControl1; 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /src/Test/Form1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Windows.Forms; 9 | 10 | namespace Test 11 | { 12 | public partial class Form1 : Form 13 | { 14 | public Form1() 15 | { 16 | InitializeComponent(); 17 | } 18 | 19 | private void winFormMPlayerControl1_Load(object sender, EventArgs e) 20 | { 21 | var player = Majorsilence.Media.Videos.PlayerFactory.Get(winFormMPlayerControl1.Handle, "/usr/bin/mplayer"); 22 | winFormMPlayerControl1.SetPlayer(player); 23 | winFormMPlayerControl1.MPlayerPath = @"/usr/bin/mplayer"; 24 | winFormMPlayerControl1.VideoPath = @"/home/peter/Downloads/20200717_183033.mp4"; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Test/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows.Forms; 5 | 6 | namespace Test 7 | { 8 | static class Program 9 | { 10 | /// 11 | /// The main entry point for the application. 12 | /// 13 | [STAThread] 14 | static void Main() 15 | { 16 | Application.EnableVisualStyles(); 17 | Application.SetCompatibleTextRenderingDefault(false); 18 | Application.Run(new Form1()); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Test/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Test.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Test.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Test/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /src/Test/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.239 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Test.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Test/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Test/Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | x86 4 | WinExe 5 | net6.0-windows;net8.0-windows;net9.0-windows 6 | enable 7 | true 8 | enable 9 | Test 10 | 11 | 12 | full 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | pdbonly 21 | 22 | 23 | Test.Test2 24 | 25 | 26 | full 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | pdbonly 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | Form1.cs 45 | 46 | 47 | Form1.cs 48 | 49 | 50 | ResXFileCodeGenerator 51 | Resources.Designer.cs 52 | Designer 53 | 54 | 55 | True 56 | Resources.resx 57 | 58 | 59 | SettingsSingleFileGenerator 60 | Settings.Designer.cs 61 | 62 | 63 | True 64 | Settings.settings 65 | True 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/Test/Test2.cs: -------------------------------------------------------------------------------- 1 |  2 | using System; 3 | using System.Windows.Forms; 4 | using Majorsilence.Media.Videos; 5 | 6 | 7 | namespace Test 8 | { 9 | static class Test2 10 | { 11 | [STAThread] 12 | static void Main() 13 | { 14 | 15 | var player = Majorsilence.Media.Videos.PlayerFactory.Get(-1, "/usr/lib/x86_64-linux-gnu/libmpv.so.1"); 16 | 17 | 18 | System.Windows.Forms.Form frm = new System.Windows.Forms.Form(); 19 | frm.Height = 600; 20 | frm.Width = 800; 21 | 22 | var playerControl = new LibMPlayerWinform.WinFormMPlayerControl(player); 23 | player.SetHandle(playerControl.Handle); 24 | playerControl.Dock = DockStyle.Fill; 25 | 26 | //playerControl.MPlayerPath = @"C:\path\to\mplayer.exe"; 27 | playerControl.VideoPath = @"/home/peter/Downloads/Die Hard 2 (1990) [1080p] {5.1}/Die.Hard.2.BluRay.1080p.x264.5.1.Judas.mp4"; 28 | 29 | frm.Controls.Add(playerControl); 30 | 31 | Application.Run(frm); 32 | } 33 | } 34 | 35 | 36 | } --------------------------------------------------------------------------------