mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-11-15 09:59:06 -07:00
Pushing missing changes
This commit is contained in:
parent
845554722e
commit
767cdc1f6f
230
.gitignore
vendored
Normal file
230
.gitignore
vendored
Normal file
@ -0,0 +1,230 @@
|
||||
#################
|
||||
## Eclipse
|
||||
#################
|
||||
|
||||
*.pydevproject
|
||||
.project
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.classpath
|
||||
.settings/
|
||||
.loadpath
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# CDT-specific
|
||||
.cproject
|
||||
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
#################
|
||||
## Media Browser
|
||||
#################
|
||||
ProgramData*/
|
||||
ProgramData-Server*/
|
||||
ProgramData-UI*/
|
||||
|
||||
#################
|
||||
## Visual Studio
|
||||
#################
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
build/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.log
|
||||
*.scc
|
||||
*.scc
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.orig
|
||||
*.rej
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.ipch
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
*.ncrunch*
|
||||
.*crunch*.local.xml
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.Publish.xml
|
||||
*.pubxml
|
||||
|
||||
# NuGet Packages Directory
|
||||
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
|
||||
#packages/
|
||||
|
||||
# Windows Azure Build Output
|
||||
csx
|
||||
*.build.csdef
|
||||
|
||||
# Windows Store app package directory
|
||||
AppPackages/
|
||||
|
||||
# Others
|
||||
sql/
|
||||
*.Cache
|
||||
ClientBin/
|
||||
[Ss]tyle[Cc]op.*
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.[Pp]ublish.xml
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file to a newer
|
||||
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
App_Data/*.mdf
|
||||
App_Data/*.ldf
|
||||
|
||||
#############
|
||||
## Windows detritus
|
||||
#############
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Mac crap
|
||||
.DS_Store
|
||||
|
||||
|
||||
#############
|
||||
## Python
|
||||
#############
|
||||
|
||||
*.py[co]
|
||||
|
||||
# Packages
|
||||
*.egg
|
||||
*.egg-info
|
||||
dist/
|
||||
build/
|
||||
eggs/
|
||||
parts/
|
||||
var/
|
||||
sdist/
|
||||
develop-eggs/
|
||||
.installed.cfg
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
.coverage
|
||||
.tox
|
||||
|
||||
#Translations
|
||||
*.mo
|
||||
|
||||
#Mr Developer
|
||||
.mr.developer.cfg
|
92
.hgignore
92
.hgignore
@ -1,43 +1,49 @@
|
||||
# use glob syntax
|
||||
syntax: glob
|
||||
|
||||
*.obj
|
||||
*.pdb
|
||||
*.user
|
||||
*.aps
|
||||
*.pch
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
*_i.c
|
||||
*_p.c
|
||||
*.ncb
|
||||
*.suo
|
||||
*.tlb
|
||||
*.tlh
|
||||
*.bak
|
||||
*.cache
|
||||
*.ilk
|
||||
*.log
|
||||
*.lib
|
||||
*.sbr
|
||||
*.scc
|
||||
*.psess
|
||||
*.vsp
|
||||
*.orig
|
||||
[Bb]in
|
||||
[Dd]ebug*/
|
||||
obj/
|
||||
[Rr]elease*/
|
||||
ProgramData*/
|
||||
ProgramData-Server*/
|
||||
ProgramData-UI*/
|
||||
_ReSharper*/
|
||||
[Tt]humbs.db
|
||||
[Tt]est[Rr]esult*
|
||||
[Bb]uild[Ll]og.*
|
||||
*.[Pp]ublish.xml
|
||||
*.resharper
|
||||
|
||||
# ncrunch files
|
||||
*.ncrunchsolution
|
||||
*.ncrunchproject
|
||||
# use glob syntax
|
||||
syntax: glob
|
||||
|
||||
*.obj
|
||||
*.pdb
|
||||
*.user
|
||||
*.aps
|
||||
*.pch
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
*_i.c
|
||||
*_p.c
|
||||
*.ncb
|
||||
*.suo
|
||||
*.tlb
|
||||
*.tlh
|
||||
*.bak
|
||||
*.cache
|
||||
*.ilk
|
||||
*.log
|
||||
*.lib
|
||||
*.sbr
|
||||
*.scc
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.orig
|
||||
*.rej
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.ipch
|
||||
[Bb]in
|
||||
[Dd]ebug*/
|
||||
obj/
|
||||
[Rr]elease*/
|
||||
ProgramData*/
|
||||
ProgramData-Server*/
|
||||
ProgramData-UI*/
|
||||
_ReSharper*/
|
||||
[Tt]humbs.db
|
||||
[Tt]est[Rr]esult*
|
||||
[Bb]uild[Ll]og.*
|
||||
*.[Pp]ublish.xml
|
||||
*.resharper
|
||||
|
||||
# ncrunch files
|
||||
*.ncrunchsolution
|
||||
*.ncrunchproject
|
||||
Setup/*
|
||||
|
5
.hgtags
Normal file
5
.hgtags
Normal file
@ -0,0 +1,5 @@
|
||||
811bcaa9490681194bd72a01c4b0f91d78a0ec97 CO Version 0.12.12.25
|
||||
811bcaa9490681194bd72a01c4b0f91d78a0ec97 CO Version 0.12.12.25
|
||||
0000000000000000000000000000000000000000 CO Version 0.12.12.25
|
||||
0000000000000000000000000000000000000000 CO Version 0.12.12.25
|
||||
e98137e38224a0d82cd7d98a71264e0cddc91ca4 CO Version 0.12.12.25
|
74
BDInfo/BDInfo.csproj
Normal file
74
BDInfo/BDInfo.csproj
Normal file
@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{07B509C0-0C28-4F3F-8963-5263281F7E3D}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>BDInfo</RootNamespace>
|
||||
<AssemblyName>BDInfo</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="BDInfoSettings.cs" />
|
||||
<Compile Include="BDROM.cs" />
|
||||
<Compile Include="LanguageCodes.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="TSCodecAC3.cs" />
|
||||
<Compile Include="TSCodecAVC.cs" />
|
||||
<Compile Include="TSCodecDTS.cs" />
|
||||
<Compile Include="TSCodecDTSHD.cs" />
|
||||
<Compile Include="TSCodecLPCM.cs" />
|
||||
<Compile Include="TSCodecMPEG2.cs" />
|
||||
<Compile Include="TSCodecMVC.cs" />
|
||||
<Compile Include="TSCodecTrueHD.cs" />
|
||||
<Compile Include="TSCodecVC1.cs" />
|
||||
<Compile Include="TSInterleavedFile.cs" />
|
||||
<Compile Include="TSPlaylistFile.cs" />
|
||||
<Compile Include="TSStream.cs" />
|
||||
<Compile Include="TSStreamBuffer.cs" />
|
||||
<Compile Include="TSStreamClip.cs" />
|
||||
<Compile Include="TSStreamClipFile.cs" />
|
||||
<Compile Include="TSStreamFile.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="ReadMe.txt" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
110
BDInfo/BDInfoSettings.cs
Normal file
110
BDInfo/BDInfoSettings.cs
Normal file
@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BDInfo
|
||||
{
|
||||
class BDInfoSettings
|
||||
{
|
||||
public static bool GenerateStreamDiagnostics
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool EnableSSIF
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool AutosaveReport
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool GenerateFrameDataFile
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool FilterLoopingPlaylists
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool FilterShortPlaylists
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static int FilterShortPlaylistsValue
|
||||
{
|
||||
get
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool UseImagePrefix
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static string UseImagePrefixValue
|
||||
{
|
||||
get
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setting this to false throws an IComparer error on some discs.
|
||||
/// </summary>
|
||||
public static bool KeepStreamOrder
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool GenerateTextSummary
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static string LastPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
482
BDInfo/BDROM.cs
Normal file
482
BDInfo/BDROM.cs
Normal file
@ -0,0 +1,482 @@
|
||||
//============================================================================
|
||||
// BDInfo - Blu-ray Video and Audio Analysis Tool
|
||||
// Copyright © 2010 Cinema Squid
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//=============================================================================
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace BDInfo
|
||||
{
|
||||
public class BDROM
|
||||
{
|
||||
public DirectoryInfo DirectoryRoot = null;
|
||||
public DirectoryInfo DirectoryBDMV = null;
|
||||
public DirectoryInfo DirectoryBDJO = null;
|
||||
public DirectoryInfo DirectoryCLIPINF = null;
|
||||
public DirectoryInfo DirectoryPLAYLIST = null;
|
||||
public DirectoryInfo DirectorySNP = null;
|
||||
public DirectoryInfo DirectorySSIF = null;
|
||||
public DirectoryInfo DirectorySTREAM = null;
|
||||
|
||||
public string VolumeLabel = null;
|
||||
public ulong Size = 0;
|
||||
public bool IsBDPlus = false;
|
||||
public bool IsBDJava = false;
|
||||
public bool IsDBOX = false;
|
||||
public bool IsPSP = false;
|
||||
public bool Is3D = false;
|
||||
public bool Is50Hz = false;
|
||||
|
||||
public Dictionary<string, TSPlaylistFile> PlaylistFiles =
|
||||
new Dictionary<string, TSPlaylistFile>();
|
||||
public Dictionary<string, TSStreamClipFile> StreamClipFiles =
|
||||
new Dictionary<string, TSStreamClipFile>();
|
||||
public Dictionary<string, TSStreamFile> StreamFiles =
|
||||
new Dictionary<string, TSStreamFile>();
|
||||
public Dictionary<string, TSInterleavedFile> InterleavedFiles =
|
||||
new Dictionary<string, TSInterleavedFile>();
|
||||
|
||||
private static List<string> ExcludeDirs = new List<string> { "ANY!", "AACS", "BDSVM", "ANYVM", "SLYVM" };
|
||||
|
||||
public delegate bool OnStreamClipFileScanError(
|
||||
TSStreamClipFile streamClipFile, Exception ex);
|
||||
|
||||
public event OnStreamClipFileScanError StreamClipFileScanError;
|
||||
|
||||
public delegate bool OnStreamFileScanError(
|
||||
TSStreamFile streamClipFile, Exception ex);
|
||||
|
||||
public event OnStreamFileScanError StreamFileScanError;
|
||||
|
||||
public delegate bool OnPlaylistFileScanError(
|
||||
TSPlaylistFile playlistFile, Exception ex);
|
||||
|
||||
public event OnPlaylistFileScanError PlaylistFileScanError;
|
||||
|
||||
public BDROM(
|
||||
string path)
|
||||
{
|
||||
//
|
||||
// Locate BDMV directories.
|
||||
//
|
||||
|
||||
DirectoryBDMV =
|
||||
GetDirectoryBDMV(path);
|
||||
|
||||
if (DirectoryBDMV == null)
|
||||
{
|
||||
throw new Exception("Unable to locate BD structure.");
|
||||
}
|
||||
|
||||
DirectoryRoot =
|
||||
DirectoryBDMV.Parent;
|
||||
DirectoryBDJO =
|
||||
GetDirectory("BDJO", DirectoryBDMV, 0);
|
||||
DirectoryCLIPINF =
|
||||
GetDirectory("CLIPINF", DirectoryBDMV, 0);
|
||||
DirectoryPLAYLIST =
|
||||
GetDirectory("PLAYLIST", DirectoryBDMV, 0);
|
||||
DirectorySNP =
|
||||
GetDirectory("SNP", DirectoryRoot, 0);
|
||||
DirectorySTREAM =
|
||||
GetDirectory("STREAM", DirectoryBDMV, 0);
|
||||
DirectorySSIF =
|
||||
GetDirectory("SSIF", DirectorySTREAM, 0);
|
||||
|
||||
if (DirectoryCLIPINF == null
|
||||
|| DirectoryPLAYLIST == null)
|
||||
{
|
||||
throw new Exception("Unable to locate BD structure.");
|
||||
}
|
||||
|
||||
//
|
||||
// Initialize basic disc properties.
|
||||
//
|
||||
|
||||
VolumeLabel = GetVolumeLabel(DirectoryRoot);
|
||||
Size = (ulong)GetDirectorySize(DirectoryRoot);
|
||||
|
||||
if (null != GetDirectory("BDSVM", DirectoryRoot, 0))
|
||||
{
|
||||
IsBDPlus = true;
|
||||
}
|
||||
if (null != GetDirectory("SLYVM", DirectoryRoot, 0))
|
||||
{
|
||||
IsBDPlus = true;
|
||||
}
|
||||
if (null != GetDirectory("ANYVM", DirectoryRoot, 0))
|
||||
{
|
||||
IsBDPlus = true;
|
||||
}
|
||||
|
||||
if (DirectoryBDJO != null &&
|
||||
DirectoryBDJO.GetFiles().Length > 0)
|
||||
{
|
||||
IsBDJava = true;
|
||||
}
|
||||
|
||||
if (DirectorySNP != null &&
|
||||
(DirectorySNP.GetFiles("*.mnv").Length > 0 || DirectorySNP.GetFiles("*.MNV").Length > 0))
|
||||
{
|
||||
IsPSP = true;
|
||||
}
|
||||
|
||||
if (DirectorySSIF != null &&
|
||||
DirectorySSIF.GetFiles().Length > 0)
|
||||
{
|
||||
Is3D = true;
|
||||
}
|
||||
|
||||
if (File.Exists(Path.Combine(DirectoryRoot.FullName, "FilmIndex.xml")))
|
||||
{
|
||||
IsDBOX = true;
|
||||
}
|
||||
|
||||
//
|
||||
// Initialize file lists.
|
||||
//
|
||||
|
||||
if (DirectoryPLAYLIST != null)
|
||||
{
|
||||
FileInfo[] files = DirectoryPLAYLIST.GetFiles("*.mpls");
|
||||
if (files.Length == 0)
|
||||
{
|
||||
files = DirectoryPLAYLIST.GetFiles("*.MPLS");
|
||||
}
|
||||
foreach (FileInfo file in files)
|
||||
{
|
||||
PlaylistFiles.Add(
|
||||
file.Name.ToUpper(), new TSPlaylistFile(this, file));
|
||||
}
|
||||
}
|
||||
|
||||
if (DirectorySTREAM != null)
|
||||
{
|
||||
FileInfo[] files = DirectorySTREAM.GetFiles("*.m2ts");
|
||||
if (files.Length == 0)
|
||||
{
|
||||
files = DirectoryPLAYLIST.GetFiles("*.M2TS");
|
||||
}
|
||||
foreach (FileInfo file in files)
|
||||
{
|
||||
StreamFiles.Add(
|
||||
file.Name.ToUpper(), new TSStreamFile(file));
|
||||
}
|
||||
}
|
||||
|
||||
if (DirectoryCLIPINF != null)
|
||||
{
|
||||
FileInfo[] files = DirectoryCLIPINF.GetFiles("*.clpi");
|
||||
if (files.Length == 0)
|
||||
{
|
||||
files = DirectoryPLAYLIST.GetFiles("*.CLPI");
|
||||
}
|
||||
foreach (FileInfo file in files)
|
||||
{
|
||||
StreamClipFiles.Add(
|
||||
file.Name.ToUpper(), new TSStreamClipFile(file));
|
||||
}
|
||||
}
|
||||
|
||||
if (DirectorySSIF != null)
|
||||
{
|
||||
FileInfo[] files = DirectorySSIF.GetFiles("*.ssif");
|
||||
if (files.Length == 0)
|
||||
{
|
||||
files = DirectorySSIF.GetFiles("*.SSIF");
|
||||
}
|
||||
foreach (FileInfo file in files)
|
||||
{
|
||||
InterleavedFiles.Add(
|
||||
file.Name.ToUpper(), new TSInterleavedFile(file));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Scan()
|
||||
{
|
||||
List<TSStreamClipFile> errorStreamClipFiles = new List<TSStreamClipFile>();
|
||||
foreach (TSStreamClipFile streamClipFile in StreamClipFiles.Values)
|
||||
{
|
||||
try
|
||||
{
|
||||
streamClipFile.Scan();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errorStreamClipFiles.Add(streamClipFile);
|
||||
if (StreamClipFileScanError != null)
|
||||
{
|
||||
if (StreamClipFileScanError(streamClipFile, ex))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (TSStreamFile streamFile in StreamFiles.Values)
|
||||
{
|
||||
string ssifName = Path.GetFileNameWithoutExtension(streamFile.Name) + ".SSIF";
|
||||
if (InterleavedFiles.ContainsKey(ssifName))
|
||||
{
|
||||
streamFile.InterleavedFile = InterleavedFiles[ssifName];
|
||||
}
|
||||
}
|
||||
|
||||
TSStreamFile[] streamFiles = new TSStreamFile[StreamFiles.Count];
|
||||
StreamFiles.Values.CopyTo(streamFiles, 0);
|
||||
Array.Sort(streamFiles, CompareStreamFiles);
|
||||
|
||||
List<TSPlaylistFile> errorPlaylistFiles = new List<TSPlaylistFile>();
|
||||
foreach (TSPlaylistFile playlistFile in PlaylistFiles.Values)
|
||||
{
|
||||
try
|
||||
{
|
||||
playlistFile.Scan(StreamFiles, StreamClipFiles);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errorPlaylistFiles.Add(playlistFile);
|
||||
if (PlaylistFileScanError != null)
|
||||
{
|
||||
if (PlaylistFileScanError(playlistFile, ex))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
List<TSStreamFile> errorStreamFiles = new List<TSStreamFile>();
|
||||
foreach (TSStreamFile streamFile in streamFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<TSPlaylistFile> playlists = new List<TSPlaylistFile>();
|
||||
foreach (TSPlaylistFile playlist in PlaylistFiles.Values)
|
||||
{
|
||||
foreach (TSStreamClip streamClip in playlist.StreamClips)
|
||||
{
|
||||
if (streamClip.Name == streamFile.Name)
|
||||
{
|
||||
playlists.Add(playlist);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
streamFile.Scan(playlists, false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errorStreamFiles.Add(streamFile);
|
||||
if (StreamFileScanError != null)
|
||||
{
|
||||
if (StreamFileScanError(streamFile, ex))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (TSPlaylistFile playlistFile in PlaylistFiles.Values)
|
||||
{
|
||||
playlistFile.Initialize();
|
||||
if (!Is50Hz)
|
||||
{
|
||||
foreach (TSVideoStream videoStream in playlistFile.VideoStreams)
|
||||
{
|
||||
if (videoStream.FrameRate == TSFrameRate.FRAMERATE_25 ||
|
||||
videoStream.FrameRate == TSFrameRate.FRAMERATE_50)
|
||||
{
|
||||
Is50Hz = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DirectoryInfo GetDirectoryBDMV(
|
||||
string path)
|
||||
{
|
||||
DirectoryInfo dir = new DirectoryInfo(path);
|
||||
|
||||
while (dir != null)
|
||||
{
|
||||
if (dir.Name == "BDMV")
|
||||
{
|
||||
return dir;
|
||||
}
|
||||
dir = dir.Parent;
|
||||
}
|
||||
|
||||
return GetDirectory("BDMV", new DirectoryInfo(path), 0);
|
||||
}
|
||||
|
||||
private DirectoryInfo GetDirectory(
|
||||
string name,
|
||||
DirectoryInfo dir,
|
||||
int searchDepth)
|
||||
{
|
||||
if (dir != null)
|
||||
{
|
||||
DirectoryInfo[] children = dir.GetDirectories();
|
||||
foreach (DirectoryInfo child in children)
|
||||
{
|
||||
if (child.Name == name)
|
||||
{
|
||||
return child;
|
||||
}
|
||||
}
|
||||
if (searchDepth > 0)
|
||||
{
|
||||
foreach (DirectoryInfo child in children)
|
||||
{
|
||||
GetDirectory(
|
||||
name, child, searchDepth - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private long GetDirectorySize(DirectoryInfo directoryInfo)
|
||||
{
|
||||
long size = 0;
|
||||
|
||||
//if (!ExcludeDirs.Contains(directoryInfo.Name.ToUpper())) // TODO: Keep?
|
||||
{
|
||||
FileInfo[] pathFiles = directoryInfo.GetFiles();
|
||||
foreach (FileInfo pathFile in pathFiles)
|
||||
{
|
||||
if (pathFile.Extension.ToUpper() == ".SSIF")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
size += pathFile.Length;
|
||||
}
|
||||
|
||||
DirectoryInfo[] pathChildren = directoryInfo.GetDirectories();
|
||||
foreach (DirectoryInfo pathChild in pathChildren)
|
||||
{
|
||||
size += GetDirectorySize(pathChild);
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
private string GetVolumeLabel(DirectoryInfo dir)
|
||||
{
|
||||
uint serialNumber = 0;
|
||||
uint maxLength = 0;
|
||||
uint volumeFlags = new uint();
|
||||
StringBuilder volumeLabel = new StringBuilder(256);
|
||||
StringBuilder fileSystemName = new StringBuilder(256);
|
||||
string label = "";
|
||||
|
||||
try
|
||||
{
|
||||
long result = GetVolumeInformation(
|
||||
dir.Name,
|
||||
volumeLabel,
|
||||
(uint)volumeLabel.Capacity,
|
||||
ref serialNumber,
|
||||
ref maxLength,
|
||||
ref volumeFlags,
|
||||
fileSystemName,
|
||||
(uint)fileSystemName.Capacity);
|
||||
|
||||
label = volumeLabel.ToString();
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (label.Length == 0)
|
||||
{
|
||||
label = dir.Name;
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
public static int CompareStreamFiles(
|
||||
TSStreamFile x,
|
||||
TSStreamFile y)
|
||||
{
|
||||
// TODO: Use interleaved file sizes
|
||||
|
||||
if ((x == null || x.FileInfo == null) && (y == null || y.FileInfo == null))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if ((x == null || x.FileInfo == null) && (y != null && y.FileInfo != null))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if ((x != null || x.FileInfo != null) && (y == null || y.FileInfo == null))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x.FileInfo.Length > y.FileInfo.Length)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (y.FileInfo.Length > x.FileInfo.Length)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern long GetVolumeInformation(
|
||||
string PathName,
|
||||
StringBuilder VolumeNameBuffer,
|
||||
uint VolumeNameSize,
|
||||
ref uint VolumeSerialNumber,
|
||||
ref uint MaximumComponentLength,
|
||||
ref uint FileSystemFlags,
|
||||
StringBuilder FileSystemNameBuffer,
|
||||
uint FileSystemNameSize);
|
||||
}
|
||||
}
|
496
BDInfo/LanguageCodes.cs
Normal file
496
BDInfo/LanguageCodes.cs
Normal file
@ -0,0 +1,496 @@
|
||||
//============================================================================
|
||||
// BDInfo - Blu-ray Video and Audio Analysis Tool
|
||||
// Copyright © 2010 Cinema Squid
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//=============================================================================
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BDInfo
|
||||
{
|
||||
public abstract class LanguageCodes
|
||||
{
|
||||
public static string GetName(string code)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case "abk": return "Abkhazian";
|
||||
case "ace": return "Achinese";
|
||||
case "ach": return "Acoli";
|
||||
case "ada": return "Adangme";
|
||||
case "aar": return "Afar";
|
||||
case "afh": return "Afrihili";
|
||||
case "afr": return "Afrikaans";
|
||||
case "afa": return "Afro-Asiatic (Other)";
|
||||
case "aka": return "Akan";
|
||||
case "akk": return "Akkadian";
|
||||
case "alb": return "Albanian";
|
||||
case "sqi": return "Albanian";
|
||||
case "ale": return "Aleut";
|
||||
case "alg": return "Algonquian languages";
|
||||
case "tut": return "Altaic (Other)";
|
||||
case "amh": return "Amharic";
|
||||
case "apa": return "Apache languages";
|
||||
case "ara": return "Arabic";
|
||||
case "arc": return "Aramaic";
|
||||
case "arp": return "Arapaho";
|
||||
case "arn": return "Araucanian";
|
||||
case "arw": return "Arawak";
|
||||
case "arm": return "Armenian";
|
||||
case "hye": return "Armenian";
|
||||
case "art": return "Artificial (Other)";
|
||||
case "asm": return "Assamese";
|
||||
case "ath": return "Athapascan languages";
|
||||
case "aus": return "Australian languages";
|
||||
case "map": return "Austronesian (Other)";
|
||||
case "ava": return "Avaric";
|
||||
case "ave": return "Avestan";
|
||||
case "awa": return "Awadhi";
|
||||
case "aym": return "Aymara";
|
||||
case "aze": return "Azerbaijani";
|
||||
case "ban": return "Balinese";
|
||||
case "bat": return "Baltic (Other)";
|
||||
case "bal": return "Baluchi";
|
||||
case "bam": return "Bambara";
|
||||
case "bai": return "Bamileke languages";
|
||||
case "bad": return "Banda";
|
||||
case "bnt": return "Bantu (Other)";
|
||||
case "bas": return "Basa";
|
||||
case "bak": return "Bashkir";
|
||||
case "baq": return "Basque";
|
||||
case "eus": return "Basque";
|
||||
case "btk": return "Batak (Indonesia)";
|
||||
case "bej": return "Beja";
|
||||
case "bel": return "Belarusian";
|
||||
case "bem": return "Bemba";
|
||||
case "ben": return "Bengali";
|
||||
case "ber": return "Berber (Other)";
|
||||
case "bho": return "Bhojpuri";
|
||||
case "bih": return "Bihari";
|
||||
case "bik": return "Bikol";
|
||||
case "bin": return "Bini";
|
||||
case "bis": return "Bislama";
|
||||
case "bos": return "Bosnian";
|
||||
case "bra": return "Braj";
|
||||
case "bre": return "Breton";
|
||||
case "bug": return "Buginese";
|
||||
case "bul": return "Bulgarian";
|
||||
case "bua": return "Buriat";
|
||||
case "bur": return "Burmese";
|
||||
case "mya": return "Burmese";
|
||||
case "cad": return "Caddo";
|
||||
case "car": return "Carib";
|
||||
case "cat": return "Catalan";
|
||||
case "cau": return "Caucasian (Other)";
|
||||
case "ceb": return "Cebuano";
|
||||
case "cel": return "Celtic (Other)";
|
||||
case "cai": return "Central American Indian (Other)";
|
||||
case "chg": return "Chagatai";
|
||||
case "cmc": return "Chamic languages";
|
||||
case "cha": return "Chamorro";
|
||||
case "che": return "Chechen";
|
||||
case "chr": return "Cherokee";
|
||||
case "chy": return "Cheyenne";
|
||||
case "chb": return "Chibcha";
|
||||
case "chi": return "Chinese";
|
||||
case "zho": return "Chinese";
|
||||
case "chn": return "Chinook jargon";
|
||||
case "chp": return "Chipewyan";
|
||||
case "cho": return "Choctaw";
|
||||
case "chu": return "Church Slavic";
|
||||
case "chk": return "Chuukese";
|
||||
case "chv": return "Chuvash";
|
||||
case "cop": return "Coptic";
|
||||
case "cor": return "Cornish";
|
||||
case "cos": return "Corsican";
|
||||
case "cre": return "Cree";
|
||||
case "mus": return "Creek";
|
||||
case "crp": return "Creoles and pidgins (Other)";
|
||||
case "cpe": return "Creoles and pidgins,";
|
||||
case "cpf": return "Creoles and pidgins,";
|
||||
case "cpp": return "Creoles and pidgins,";
|
||||
case "scr": return "Croatian";
|
||||
case "hrv": return "Croatian";
|
||||
case "cus": return "Cushitic (Other)";
|
||||
case "cze": return "Czech";
|
||||
case "ces": return "Czech";
|
||||
case "dak": return "Dakota";
|
||||
case "dan": return "Danish";
|
||||
case "day": return "Dayak";
|
||||
case "del": return "Delaware";
|
||||
case "din": return "Dinka";
|
||||
case "div": return "Divehi";
|
||||
case "doi": return "Dogri";
|
||||
case "dgr": return "Dogrib";
|
||||
case "dra": return "Dravidian (Other)";
|
||||
case "dua": return "Duala";
|
||||
case "dut": return "Dutch";
|
||||
case "nld": return "Dutch";
|
||||
case "dum": return "Dutch, Middle (ca. 1050-1350)";
|
||||
case "dyu": return "Dyula";
|
||||
case "dzo": return "Dzongkha";
|
||||
case "efi": return "Efik";
|
||||
case "egy": return "Egyptian (Ancient)";
|
||||
case "eka": return "Ekajuk";
|
||||
case "elx": return "Elamite";
|
||||
case "eng": return "English";
|
||||
case "enm": return "English, Middle (1100-1500)";
|
||||
case "ang": return "English, Old (ca.450-1100)";
|
||||
case "epo": return "Esperanto";
|
||||
case "est": return "Estonian";
|
||||
case "ewe": return "Ewe";
|
||||
case "ewo": return "Ewondo";
|
||||
case "fan": return "Fang";
|
||||
case "fat": return "Fanti";
|
||||
case "fao": return "Faroese";
|
||||
case "fij": return "Fijian";
|
||||
case "fin": return "Finnish";
|
||||
case "fiu": return "Finno-Ugrian (Other)";
|
||||
case "fon": return "Fon";
|
||||
case "fre": return "French";
|
||||
case "fra": return "French";
|
||||
case "frm": return "French, Middle (ca.1400-1600)";
|
||||
case "fro": return "French, Old (842-ca.1400)";
|
||||
case "fry": return "Frisian";
|
||||
case "fur": return "Friulian";
|
||||
case "ful": return "Fulah";
|
||||
case "gaa": return "Ga";
|
||||
case "glg": return "Gallegan";
|
||||
case "lug": return "Ganda";
|
||||
case "gay": return "Gayo";
|
||||
case "gba": return "Gbaya";
|
||||
case "gez": return "Geez";
|
||||
case "geo": return "Georgian";
|
||||
case "kat": return "Georgian";
|
||||
case "ger": return "German";
|
||||
case "deu": return "German";
|
||||
case "nds": return "Saxon";
|
||||
case "gmh": return "German, Middle High (ca.1050-1500)";
|
||||
case "goh": return "German, Old High (ca.750-1050)";
|
||||
case "gem": return "Germanic (Other)";
|
||||
case "gil": return "Gilbertese";
|
||||
case "gon": return "Gondi";
|
||||
case "gor": return "Gorontalo";
|
||||
case "got": return "Gothic";
|
||||
case "grb": return "Grebo";
|
||||
case "grc": return "Greek, Ancient (to 1453)";
|
||||
case "gre": return "Greek";
|
||||
case "ell": return "Greek";
|
||||
case "grn": return "Guarani";
|
||||
case "guj": return "Gujarati";
|
||||
case "gwi": return "Gwich´in";
|
||||
case "hai": return "Haida";
|
||||
case "hau": return "Hausa";
|
||||
case "haw": return "Hawaiian";
|
||||
case "heb": return "Hebrew";
|
||||
case "her": return "Herero";
|
||||
case "hil": return "Hiligaynon";
|
||||
case "him": return "Himachali";
|
||||
case "hin": return "Hindi";
|
||||
case "hmo": return "Hiri Motu";
|
||||
case "hit": return "Hittite";
|
||||
case "hmn": return "Hmong";
|
||||
case "hun": return "Hungarian";
|
||||
case "hup": return "Hupa";
|
||||
case "iba": return "Iban";
|
||||
case "ice": return "Icelandic";
|
||||
case "isl": return "Icelandic";
|
||||
case "ibo": return "Igbo";
|
||||
case "ijo": return "Ijo";
|
||||
case "ilo": return "Iloko";
|
||||
case "inc": return "Indic (Other)";
|
||||
case "ine": return "Indo-European (Other)";
|
||||
case "ind": return "Indonesian";
|
||||
case "ina": return "Interlingua (International";
|
||||
case "ile": return "Interlingue";
|
||||
case "iku": return "Inuktitut";
|
||||
case "ipk": return "Inupiaq";
|
||||
case "ira": return "Iranian (Other)";
|
||||
case "gle": return "Irish";
|
||||
case "mga": return "Irish, Middle (900-1200)";
|
||||
case "sga": return "Irish, Old (to 900)";
|
||||
case "iro": return "Iroquoian languages";
|
||||
case "ita": return "Italian";
|
||||
case "jpn": return "Japanese";
|
||||
case "jav": return "Javanese";
|
||||
case "jrb": return "Judeo-Arabic";
|
||||
case "jpr": return "Judeo-Persian";
|
||||
case "kab": return "Kabyle";
|
||||
case "kac": return "Kachin";
|
||||
case "kal": return "Kalaallisut";
|
||||
case "kam": return "Kamba";
|
||||
case "kan": return "Kannada";
|
||||
case "kau": return "Kanuri";
|
||||
case "kaa": return "Kara-Kalpak";
|
||||
case "kar": return "Karen";
|
||||
case "kas": return "Kashmiri";
|
||||
case "kaw": return "Kawi";
|
||||
case "kaz": return "Kazakh";
|
||||
case "kha": return "Khasi";
|
||||
case "khm": return "Khmer";
|
||||
case "khi": return "Khoisan (Other)";
|
||||
case "kho": return "Khotanese";
|
||||
case "kik": return "Kikuyu";
|
||||
case "kmb": return "Kimbundu";
|
||||
case "kin": return "Kinyarwanda";
|
||||
case "kir": return "Kirghiz";
|
||||
case "kom": return "Komi";
|
||||
case "kon": return "Kongo";
|
||||
case "kok": return "Konkani";
|
||||
case "kor": return "Korean";
|
||||
case "kos": return "Kosraean";
|
||||
case "kpe": return "Kpelle";
|
||||
case "kro": return "Kru";
|
||||
case "kua": return "Kuanyama";
|
||||
case "kum": return "Kumyk";
|
||||
case "kur": return "Kurdish";
|
||||
case "kru": return "Kurukh";
|
||||
case "kut": return "Kutenai";
|
||||
case "lad": return "Ladino";
|
||||
case "lah": return "Lahnda";
|
||||
case "lam": return "Lamba";
|
||||
case "lao": return "Lao";
|
||||
case "lat": return "Latin";
|
||||
case "lav": return "Latvian";
|
||||
case "ltz": return "Letzeburgesch";
|
||||
case "lez": return "Lezghian";
|
||||
case "lin": return "Lingala";
|
||||
case "lit": return "Lithuanian";
|
||||
case "loz": return "Lozi";
|
||||
case "lub": return "Luba-Katanga";
|
||||
case "lua": return "Luba-Lulua";
|
||||
case "lui": return "Luiseno";
|
||||
case "lun": return "Lunda";
|
||||
case "luo": return "Luo (Kenya and Tanzania)";
|
||||
case "lus": return "Lushai";
|
||||
case "mac": return "Macedonian";
|
||||
case "mkd": return "Macedonian";
|
||||
case "mad": return "Madurese";
|
||||
case "mag": return "Magahi";
|
||||
case "mai": return "Maithili";
|
||||
case "mak": return "Makasar";
|
||||
case "mlg": return "Malagasy";
|
||||
case "may": return "Malay";
|
||||
case "msa": return "Malay";
|
||||
case "mal": return "Malayalam";
|
||||
case "mlt": return "Maltese";
|
||||
case "mnc": return "Manchu";
|
||||
case "mdr": return "Mandar";
|
||||
case "man": return "Mandingo";
|
||||
case "mni": return "Manipuri";
|
||||
case "mno": return "Manobo languages";
|
||||
case "glv": return "Manx";
|
||||
case "mao": return "Maori";
|
||||
case "mri": return "Maori";
|
||||
case "mar": return "Marathi";
|
||||
case "chm": return "Mari";
|
||||
case "mah": return "Marshall";
|
||||
case "mwr": return "Marwari";
|
||||
case "mas": return "Masai";
|
||||
case "myn": return "Mayan languages";
|
||||
case "men": return "Mende";
|
||||
case "mic": return "Micmac";
|
||||
case "min": return "Minangkabau";
|
||||
case "mis": return "Miscellaneous languages";
|
||||
case "moh": return "Mohawk";
|
||||
case "mol": return "Moldavian";
|
||||
case "mkh": return "Mon-Khmer (Other)";
|
||||
case "lol": return "Mongo";
|
||||
case "mon": return "Mongolian";
|
||||
case "mos": return "Mossi";
|
||||
case "mul": return "Multiple languages";
|
||||
case "mun": return "Munda languages";
|
||||
case "nah": return "Nahuatl";
|
||||
case "nau": return "Nauru";
|
||||
case "nav": return "Navajo";
|
||||
case "nde": return "Ndebele, North";
|
||||
case "nbl": return "Ndebele, South";
|
||||
case "ndo": return "Ndonga";
|
||||
case "nep": return "Nepali";
|
||||
case "new": return "Newari";
|
||||
case "nia": return "Nias";
|
||||
case "nic": return "Niger-Kordofanian (Other)";
|
||||
case "ssa": return "Nilo-Saharan (Other)";
|
||||
case "niu": return "Niuean";
|
||||
case "non": return "Norse, Old";
|
||||
case "nai": return "North American Indian (Other)";
|
||||
case "sme": return "Northern Sami";
|
||||
case "nor": return "Norwegian";
|
||||
case "nob": return "Norwegian Bokmål";
|
||||
case "nno": return "Norwegian Nynorsk";
|
||||
case "nub": return "Nubian languages";
|
||||
case "nym": return "Nyamwezi";
|
||||
case "nya": return "Nyanja";
|
||||
case "nyn": return "Nyankole";
|
||||
case "nyo": return "Nyoro";
|
||||
case "nzi": return "Nzima";
|
||||
case "oci": return "Occitan";
|
||||
case "oji": return "Ojibwa";
|
||||
case "ori": return "Oriya";
|
||||
case "orm": return "Oromo";
|
||||
case "osa": return "Osage";
|
||||
case "oss": return "Ossetian";
|
||||
case "oto": return "Otomian languages";
|
||||
case "pal": return "Pahlavi";
|
||||
case "pau": return "Palauan";
|
||||
case "pli": return "Pali";
|
||||
case "pam": return "Pampanga";
|
||||
case "pag": return "Pangasinan";
|
||||
case "pan": return "Panjabi";
|
||||
case "pap": return "Papiamento";
|
||||
case "paa": return "Papuan (Other)";
|
||||
case "per": return "Persian";
|
||||
case "fas": return "Persian";
|
||||
case "peo": return "Persian, Old (ca.600-400 B.C.)";
|
||||
case "phi": return "Philippine (Other)";
|
||||
case "phn": return "Phoenician";
|
||||
case "pon": return "Pohnpeian";
|
||||
case "pol": return "Polish";
|
||||
case "por": return "Portuguese";
|
||||
case "pra": return "Prakrit languages";
|
||||
case "pro": return "Provençal";
|
||||
case "pus": return "Pushto";
|
||||
case "que": return "Quechua";
|
||||
case "roh": return "Raeto-Romance";
|
||||
case "raj": return "Rajasthani";
|
||||
case "rap": return "Rapanui";
|
||||
case "rar": return "Rarotongan";
|
||||
case "roa": return "Romance (Other)";
|
||||
case "rum": return "Romanian";
|
||||
case "ron": return "Romanian";
|
||||
case "rom": return "Romany";
|
||||
case "run": return "Rundi";
|
||||
case "rus": return "Russian";
|
||||
case "sal": return "Salishan languages";
|
||||
case "sam": return "Samaritan Aramaic";
|
||||
case "smi": return "Sami languages (Other)";
|
||||
case "smo": return "Samoan";
|
||||
case "sad": return "Sandawe";
|
||||
case "sag": return "Sango";
|
||||
case "san": return "Sanskrit";
|
||||
case "sat": return "Santali";
|
||||
case "srd": return "Sardinian";
|
||||
case "sas": return "Sasak";
|
||||
case "sco": return "Scots";
|
||||
case "gla": return "Gaelic";
|
||||
case "sel": return "Selkup";
|
||||
case "sem": return "Semitic (Other)";
|
||||
case "scc": return "Serbian";
|
||||
case "srp": return "Serbian";
|
||||
case "srr": return "Serer";
|
||||
case "shn": return "Shan";
|
||||
case "sna": return "Shona";
|
||||
case "sid": return "Sidamo";
|
||||
case "sgn": return "Sign languages";
|
||||
case "bla": return "Siksika";
|
||||
case "snd": return "Sindhi";
|
||||
case "sin": return "Sinhalese";
|
||||
case "sit": return "Sino-Tibetan (Other)";
|
||||
case "sio": return "Siouan languages";
|
||||
case "den": return "Slave (Athapascan)";
|
||||
case "sla": return "Slavic (Other)";
|
||||
case "slo": return "Slovak";
|
||||
case "slk": return "Slovak";
|
||||
case "slv": return "Slovenian";
|
||||
case "sog": return "Sogdian";
|
||||
case "som": return "Somali";
|
||||
case "son": return "Songhai";
|
||||
case "snk": return "Soninke";
|
||||
case "wen": return "Sorbian languages";
|
||||
case "nso": return "Sotho, Northern";
|
||||
case "sot": return "Sotho, Southern";
|
||||
case "sai": return "South American Indian (Other)";
|
||||
case "spa": return "Spanish";
|
||||
case "suk": return "Sukuma";
|
||||
case "sux": return "Sumerian";
|
||||
case "sun": return "Sundanese";
|
||||
case "sus": return "Susu";
|
||||
case "swa": return "Swahili";
|
||||
case "ssw": return "Swati";
|
||||
case "swe": return "Swedish";
|
||||
case "syr": return "Syriac";
|
||||
case "tgl": return "Tagalog";
|
||||
case "tah": return "Tahitian";
|
||||
case "tai": return "Tai (Other)";
|
||||
case "tgk": return "Tajik";
|
||||
case "tmh": return "Tamashek";
|
||||
case "tam": return "Tamil";
|
||||
case "tat": return "Tatar";
|
||||
case "tel": return "Telugu";
|
||||
case "ter": return "Tereno";
|
||||
case "tet": return "Tetum";
|
||||
case "tha": return "Thai";
|
||||
case "tib": return "Tibetan";
|
||||
case "bod": return "Tibetan";
|
||||
case "tig": return "Tigre";
|
||||
case "tir": return "Tigrinya";
|
||||
case "tem": return "Timne";
|
||||
case "tiv": return "Tiv";
|
||||
case "tli": return "Tlingit";
|
||||
case "tpi": return "Tok Pisin";
|
||||
case "tkl": return "Tokelau";
|
||||
case "tog": return "Tonga (Nyasa)";
|
||||
case "ton": return "Tonga (Tonga Islands)";
|
||||
case "tsi": return "Tsimshian";
|
||||
case "tso": return "Tsonga";
|
||||
case "tsn": return "Tswana";
|
||||
case "tum": return "Tumbuka";
|
||||
case "tur": return "Turkish";
|
||||
case "ota": return "Turkish, Ottoman (1500-1928)";
|
||||
case "tuk": return "Turkmen";
|
||||
case "tvl": return "Tuvalu";
|
||||
case "tyv": return "Tuvinian";
|
||||
case "twi": return "Twi";
|
||||
case "uga": return "Ugaritic";
|
||||
case "uig": return "Uighur";
|
||||
case "ukr": return "Ukrainian";
|
||||
case "umb": return "Umbundu";
|
||||
case "und": return "Undetermined";
|
||||
case "urd": return "Urdu";
|
||||
case "uzb": return "Uzbek";
|
||||
case "vai": return "Vai";
|
||||
case "ven": return "Venda";
|
||||
case "vie": return "Vietnamese";
|
||||
case "vol": return "Volapük";
|
||||
case "vot": return "Votic";
|
||||
case "wak": return "Wakashan languages";
|
||||
case "wal": return "Walamo";
|
||||
case "war": return "Waray";
|
||||
case "was": return "Washo";
|
||||
case "wel": return "Welsh";
|
||||
case "cym": return "Welsh";
|
||||
case "wol": return "Wolof";
|
||||
case "xho": return "Xhosa";
|
||||
case "sah": return "Yakut";
|
||||
case "yao": return "Yao";
|
||||
case "yap": return "Yapese";
|
||||
case "yid": return "Yiddish";
|
||||
case "yor": return "Yoruba";
|
||||
case "ypk": return "Yupik languages";
|
||||
case "znd": return "Zande";
|
||||
case "zap": return "Zapotec";
|
||||
case "zen": return "Zenaga";
|
||||
case "zha": return "Zhuang";
|
||||
case "zul": return "Zulu";
|
||||
case "zun": return "Zuni";
|
||||
|
||||
default: return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
36
BDInfo/Properties/AssemblyInfo.cs
Normal file
36
BDInfo/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("BDInfo")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("BDInfo")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2012")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("2ecb9fe5-e2da-4b49-880b-d9887a84c311")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
5
BDInfo/ReadMe.txt
Normal file
5
BDInfo/ReadMe.txt
Normal file
@ -0,0 +1,5 @@
|
||||
The source is taken from the BDRom folder of this project:
|
||||
|
||||
http://www.cinemasquid.com/blu-ray/tools/bdinfo
|
||||
|
||||
BDInfoSettings was taken from the FormSettings class, and changed so that the settings all return defaults.
|
312
BDInfo/TSCodecAC3.cs
Normal file
312
BDInfo/TSCodecAC3.cs
Normal file
@ -0,0 +1,312 @@
|
||||
//============================================================================
|
||||
// BDInfo - Blu-ray Video and Audio Analysis Tool
|
||||
// Copyright © 2010 Cinema Squid
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//=============================================================================
|
||||
|
||||
#undef DEBUG
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BDInfo
|
||||
{
|
||||
public abstract class TSCodecAC3
|
||||
{
|
||||
private static byte[] eac3_blocks = new byte[] { 1, 2, 3, 6 };
|
||||
|
||||
public static void Scan(
|
||||
TSAudioStream stream,
|
||||
TSStreamBuffer buffer,
|
||||
ref string tag)
|
||||
{
|
||||
if (stream.IsInitialized) return;
|
||||
|
||||
byte[] sync = buffer.ReadBytes(2);
|
||||
if (sync == null ||
|
||||
sync[0] != 0x0B ||
|
||||
sync[1] != 0x77)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int sr_code = 0;
|
||||
int frame_size = 0;
|
||||
int frame_size_code = 0;
|
||||
int channel_mode = 0;
|
||||
int lfe_on = 0;
|
||||
int dial_norm = 0;
|
||||
int num_blocks = 0;
|
||||
|
||||
byte[] hdr = buffer.ReadBytes(4);
|
||||
int bsid = (hdr[3] & 0xF8) >> 3;
|
||||
buffer.Seek(-4, SeekOrigin.Current);
|
||||
if (bsid <= 10)
|
||||
{
|
||||
byte[] crc = buffer.ReadBytes(2);
|
||||
sr_code = buffer.ReadBits(2);
|
||||
frame_size_code = buffer.ReadBits(6);
|
||||
bsid = buffer.ReadBits(5);
|
||||
int bsmod = buffer.ReadBits(3);
|
||||
|
||||
channel_mode = buffer.ReadBits(3);
|
||||
int cmixlev = 0;
|
||||
if (((channel_mode & 0x1) > 0) && (channel_mode != 0x1))
|
||||
{
|
||||
cmixlev = buffer.ReadBits(2);
|
||||
}
|
||||
int surmixlev = 0;
|
||||
if ((channel_mode & 0x4) > 0)
|
||||
{
|
||||
surmixlev = buffer.ReadBits(2);
|
||||
}
|
||||
int dsurmod = 0;
|
||||
if (channel_mode == 0x2)
|
||||
{
|
||||
dsurmod = buffer.ReadBits(2);
|
||||
if (dsurmod == 0x2)
|
||||
{
|
||||
stream.AudioMode = TSAudioMode.Surround;
|
||||
}
|
||||
}
|
||||
lfe_on = buffer.ReadBits(1);
|
||||
dial_norm = buffer.ReadBits(5);
|
||||
int compr = 0;
|
||||
if (1 == buffer.ReadBits(1))
|
||||
{
|
||||
compr = buffer.ReadBits(8);
|
||||
}
|
||||
int langcod = 0;
|
||||
if (1 == buffer.ReadBits(1))
|
||||
{
|
||||
langcod = buffer.ReadBits(8);
|
||||
}
|
||||
int mixlevel = 0;
|
||||
int roomtyp = 0;
|
||||
if (1 == buffer.ReadBits(1))
|
||||
{
|
||||
mixlevel = buffer.ReadBits(5);
|
||||
roomtyp = buffer.ReadBits(2);
|
||||
}
|
||||
if (channel_mode == 0)
|
||||
{
|
||||
int dialnorm2 = buffer.ReadBits(5);
|
||||
int compr2 = 0;
|
||||
if (1 == buffer.ReadBits(1))
|
||||
{
|
||||
compr2 = buffer.ReadBits(8);
|
||||
}
|
||||
int langcod2 = 0;
|
||||
if (1 == buffer.ReadBits(1))
|
||||
{
|
||||
langcod2 = buffer.ReadBits(8);
|
||||
}
|
||||
int mixlevel2 = 0;
|
||||
int roomtyp2 = 0;
|
||||
if (1 == buffer.ReadBits(1))
|
||||
{
|
||||
mixlevel2 = buffer.ReadBits(5);
|
||||
roomtyp2 = buffer.ReadBits(2);
|
||||
}
|
||||
}
|
||||
int copyrightb = buffer.ReadBits(1);
|
||||
int origbs = buffer.ReadBits(1);
|
||||
if (bsid == 6)
|
||||
{
|
||||
if (1 == buffer.ReadBits(1))
|
||||
{
|
||||
int dmixmod = buffer.ReadBits(2);
|
||||
int ltrtcmixlev = buffer.ReadBits(3);
|
||||
int ltrtsurmixlev = buffer.ReadBits(3);
|
||||
int lorocmixlev = buffer.ReadBits(3);
|
||||
int lorosurmixlev = buffer.ReadBits(3);
|
||||
}
|
||||
if (1 == buffer.ReadBits(1))
|
||||
{
|
||||
int dsurexmod = buffer.ReadBits(2);
|
||||
int dheadphonmod = buffer.ReadBits(2);
|
||||
if (dheadphonmod == 0x2)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
int adconvtyp = buffer.ReadBits(1);
|
||||
int xbsi2 = buffer.ReadBits(8);
|
||||
int encinfo = buffer.ReadBits(1);
|
||||
if (dsurexmod == 2)
|
||||
{
|
||||
stream.AudioMode = TSAudioMode.Extended;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int frame_type = buffer.ReadBits(2);
|
||||
int substreamid = buffer.ReadBits(3);
|
||||
frame_size = (buffer.ReadBits(11) + 1) << 1;
|
||||
|
||||
sr_code = buffer.ReadBits(2);
|
||||
if (sr_code == 3)
|
||||
{
|
||||
sr_code = buffer.ReadBits(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
num_blocks = buffer.ReadBits(2);
|
||||
}
|
||||
channel_mode = buffer.ReadBits(3);
|
||||
lfe_on = buffer.ReadBits(1);
|
||||
}
|
||||
|
||||
switch (channel_mode)
|
||||
{
|
||||
case 0: // 1+1
|
||||
stream.ChannelCount = 2;
|
||||
if (stream.AudioMode == TSAudioMode.Unknown)
|
||||
{
|
||||
stream.AudioMode = TSAudioMode.DualMono;
|
||||
}
|
||||
break;
|
||||
case 1: // 1/0
|
||||
stream.ChannelCount = 1;
|
||||
break;
|
||||
case 2: // 2/0
|
||||
stream.ChannelCount = 2;
|
||||
if (stream.AudioMode == TSAudioMode.Unknown)
|
||||
{
|
||||
stream.AudioMode = TSAudioMode.Stereo;
|
||||
}
|
||||
break;
|
||||
case 3: // 3/0
|
||||
stream.ChannelCount = 3;
|
||||
break;
|
||||
case 4: // 2/1
|
||||
stream.ChannelCount = 3;
|
||||
break;
|
||||
case 5: // 3/1
|
||||
stream.ChannelCount = 4;
|
||||
break;
|
||||
case 6: // 2/2
|
||||
stream.ChannelCount = 4;
|
||||
break;
|
||||
case 7: // 3/2
|
||||
stream.ChannelCount = 5;
|
||||
break;
|
||||
default:
|
||||
stream.ChannelCount = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (sr_code)
|
||||
{
|
||||
case 0:
|
||||
stream.SampleRate = 48000;
|
||||
break;
|
||||
case 1:
|
||||
stream.SampleRate = 44100;
|
||||
break;
|
||||
case 2:
|
||||
stream.SampleRate = 32000;
|
||||
break;
|
||||
default:
|
||||
stream.SampleRate = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (bsid <= 10)
|
||||
{
|
||||
switch (frame_size_code >> 1)
|
||||
{
|
||||
case 18:
|
||||
stream.BitRate = 640000;
|
||||
break;
|
||||
case 17:
|
||||
stream.BitRate = 576000;
|
||||
break;
|
||||
case 16:
|
||||
stream.BitRate = 512000;
|
||||
break;
|
||||
case 15:
|
||||
stream.BitRate = 448000;
|
||||
break;
|
||||
case 14:
|
||||
stream.BitRate = 384000;
|
||||
break;
|
||||
case 13:
|
||||
stream.BitRate = 320000;
|
||||
break;
|
||||
case 12:
|
||||
stream.BitRate = 256000;
|
||||
break;
|
||||
case 11:
|
||||
stream.BitRate = 224000;
|
||||
break;
|
||||
case 10:
|
||||
stream.BitRate = 192000;
|
||||
break;
|
||||
case 9:
|
||||
stream.BitRate = 160000;
|
||||
break;
|
||||
case 8:
|
||||
stream.BitRate = 128000;
|
||||
break;
|
||||
case 7:
|
||||
stream.BitRate = 112000;
|
||||
break;
|
||||
case 6:
|
||||
stream.BitRate = 96000;
|
||||
break;
|
||||
case 5:
|
||||
stream.BitRate = 80000;
|
||||
break;
|
||||
case 4:
|
||||
stream.BitRate = 64000;
|
||||
break;
|
||||
case 3:
|
||||
stream.BitRate = 56000;
|
||||
break;
|
||||
case 2:
|
||||
stream.BitRate = 48000;
|
||||
break;
|
||||
case 1:
|
||||
stream.BitRate = 40000;
|
||||
break;
|
||||
case 0:
|
||||
stream.BitRate = 32000;
|
||||
break;
|
||||
default:
|
||||
stream.BitRate = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.BitRate = (long)
|
||||
(4.0 * frame_size * stream.SampleRate / (num_blocks * 256));
|
||||
}
|
||||
|
||||
stream.LFE = lfe_on;
|
||||
if (stream.StreamType != TSStreamType.AC3_PLUS_AUDIO &&
|
||||
stream.StreamType != TSStreamType.AC3_PLUS_SECONDARY_AUDIO)
|
||||
{
|
||||
stream.DialNorm = dial_norm - 31;
|
||||
}
|
||||
stream.IsVBR = false;
|
||||
stream.IsInitialized = true;
|
||||
}
|
||||
}
|
||||
}
|
151
BDInfo/TSCodecAVC.cs
Normal file
151
BDInfo/TSCodecAVC.cs
Normal file
@ -0,0 +1,151 @@
|
||||
//============================================================================
|
||||
// BDInfo - Blu-ray Video and Audio Analysis Tool
|
||||
// Copyright © 2010 Cinema Squid
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//=============================================================================
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BDInfo
|
||||
{
|
||||
public abstract class TSCodecAVC
|
||||
{
|
||||
public static void Scan(
|
||||
TSVideoStream stream,
|
||||
TSStreamBuffer buffer,
|
||||
ref string tag)
|
||||
{
|
||||
uint parse = 0;
|
||||
byte accessUnitDelimiterParse = 0;
|
||||
byte sequenceParameterSetParse = 0;
|
||||
string profile = null;
|
||||
string level = null;
|
||||
byte constraintSet0Flag = 0;
|
||||
byte constraintSet1Flag = 0;
|
||||
byte constraintSet2Flag = 0;
|
||||
byte constraintSet3Flag = 0;
|
||||
|
||||
for (int i = 0; i < buffer.Length; i++)
|
||||
{
|
||||
parse = (parse << 8) + buffer.ReadByte();
|
||||
|
||||
if (parse == 0x00000109)
|
||||
{
|
||||
accessUnitDelimiterParse = 1;
|
||||
}
|
||||
else if (accessUnitDelimiterParse > 0)
|
||||
{
|
||||
--accessUnitDelimiterParse;
|
||||
if (accessUnitDelimiterParse == 0)
|
||||
{
|
||||
switch ((parse & 0xFF) >> 5)
|
||||
{
|
||||
case 0: // I
|
||||
case 3: // SI
|
||||
case 5: // I, SI
|
||||
tag = "I";
|
||||
break;
|
||||
|
||||
case 1: // I, P
|
||||
case 4: // SI, SP
|
||||
case 6: // I, SI, P, SP
|
||||
tag = "P";
|
||||
break;
|
||||
|
||||
case 2: // I, P, B
|
||||
case 7: // I, SI, P, SP, B
|
||||
tag = "B";
|
||||
break;
|
||||
}
|
||||
if (stream.IsInitialized) return;
|
||||
}
|
||||
}
|
||||
else if (parse == 0x00000127 || parse == 0x00000167)
|
||||
{
|
||||
sequenceParameterSetParse = 3;
|
||||
}
|
||||
else if (sequenceParameterSetParse > 0)
|
||||
{
|
||||
--sequenceParameterSetParse;
|
||||
switch (sequenceParameterSetParse)
|
||||
{
|
||||
case 2:
|
||||
switch (parse & 0xFF)
|
||||
{
|
||||
case 66:
|
||||
profile = "Baseline Profile";
|
||||
break;
|
||||
case 77:
|
||||
profile = "Main Profile";
|
||||
break;
|
||||
case 88:
|
||||
profile = "Extended Profile";
|
||||
break;
|
||||
case 100:
|
||||
profile = "High Profile";
|
||||
break;
|
||||
case 110:
|
||||
profile = "High 10 Profile";
|
||||
break;
|
||||
case 122:
|
||||
profile = "High 4:2:2 Profile";
|
||||
break;
|
||||
case 144:
|
||||
profile = "High 4:4:4 Profile";
|
||||
break;
|
||||
default:
|
||||
profile = "Unknown Profile";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
constraintSet0Flag = (byte)
|
||||
((parse & 0x80) >> 7);
|
||||
constraintSet1Flag = (byte)
|
||||
((parse & 0x40) >> 6);
|
||||
constraintSet2Flag = (byte)
|
||||
((parse & 0x20) >> 5);
|
||||
constraintSet3Flag = (byte)
|
||||
((parse & 0x10) >> 4);
|
||||
break;
|
||||
|
||||
case 0:
|
||||
byte b = (byte)(parse & 0xFF);
|
||||
if (b == 11 && constraintSet3Flag == 1)
|
||||
{
|
||||
level = "1b";
|
||||
}
|
||||
else
|
||||
{
|
||||
level = string.Format(
|
||||
"{0:D}.{1:D}",
|
||||
b / 10, (b - ((b / 10) * 10)));
|
||||
}
|
||||
stream.EncodingProfile = string.Format(
|
||||
"{0} {1}", profile, level);
|
||||
stream.IsVBR = true;
|
||||
stream.IsInitialized = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
162
BDInfo/TSCodecDTS.cs
Normal file
162
BDInfo/TSCodecDTS.cs
Normal file
@ -0,0 +1,162 @@
|
||||
//============================================================================
|
||||
// BDInfo - Blu-ray Video and Audio Analysis Tool
|
||||
// Copyright © 2010 Cinema Squid
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//=============================================================================
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BDInfo
|
||||
{
|
||||
public abstract class TSCodecDTS
|
||||
{
|
||||
private static int[] dca_sample_rates =
|
||||
{
|
||||
0, 8000, 16000, 32000, 0, 0, 11025, 22050, 44100, 0, 0,
|
||||
12000, 24000, 48000, 96000, 192000
|
||||
};
|
||||
|
||||
private static int[] dca_bit_rates =
|
||||
{
|
||||
32000, 56000, 64000, 96000, 112000, 128000,
|
||||
192000, 224000, 256000, 320000, 384000,
|
||||
448000, 512000, 576000, 640000, 768000,
|
||||
896000, 1024000, 1152000, 1280000, 1344000,
|
||||
1408000, 1411200, 1472000, 1509000, 1920000,
|
||||
2048000, 3072000, 3840000, 1/*open*/, 2/*variable*/, 3/*lossless*/
|
||||
};
|
||||
|
||||
private static int[] dca_channels =
|
||||
{
|
||||
1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7, 8, 8
|
||||
};
|
||||
|
||||
private static int[] dca_bits_per_sample =
|
||||
{
|
||||
16, 16, 20, 20, 0, 24, 24
|
||||
};
|
||||
|
||||
public static void Scan(
|
||||
TSAudioStream stream,
|
||||
TSStreamBuffer buffer,
|
||||
long bitrate,
|
||||
ref string tag)
|
||||
{
|
||||
if (stream.IsInitialized) return;
|
||||
|
||||
bool syncFound = false;
|
||||
uint sync = 0;
|
||||
for (int i = 0; i < buffer.Length; i++)
|
||||
{
|
||||
sync = (sync << 8) + buffer.ReadByte();
|
||||
if (sync == 0x7FFE8001)
|
||||
{
|
||||
syncFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!syncFound) return;
|
||||
|
||||
int frame_type = buffer.ReadBits(1);
|
||||
int samples_deficit = buffer.ReadBits(5);
|
||||
int crc_present = buffer.ReadBits(1);
|
||||
int sample_blocks = buffer.ReadBits(7);
|
||||
int frame_size = buffer.ReadBits(14);
|
||||
if (frame_size < 95)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int amode = buffer.ReadBits(6);
|
||||
int sample_rate = buffer.ReadBits(4);
|
||||
if (sample_rate < 0 || sample_rate >= dca_sample_rates.Length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int bit_rate = buffer.ReadBits(5);
|
||||
if (bit_rate < 0 || bit_rate >= dca_bit_rates.Length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int downmix = buffer.ReadBits(1);
|
||||
int dynrange = buffer.ReadBits(1);
|
||||
int timestamp = buffer.ReadBits(1);
|
||||
int aux_data = buffer.ReadBits(1);
|
||||
int hdcd = buffer.ReadBits(1);
|
||||
int ext_descr = buffer.ReadBits(3);
|
||||
int ext_coding = buffer.ReadBits(1);
|
||||
int aspf = buffer.ReadBits(1);
|
||||
int lfe = buffer.ReadBits(2);
|
||||
int predictor_history = buffer.ReadBits(1);
|
||||
if (crc_present == 1)
|
||||
{
|
||||
int crc = buffer.ReadBits(16);
|
||||
}
|
||||
int multirate_inter = buffer.ReadBits(1);
|
||||
int version = buffer.ReadBits(4);
|
||||
int copy_history = buffer.ReadBits(2);
|
||||
int source_pcm_res = buffer.ReadBits(3);
|
||||
int front_sum = buffer.ReadBits(1);
|
||||
int surround_sum = buffer.ReadBits(1);
|
||||
int dialog_norm = buffer.ReadBits(4);
|
||||
if (source_pcm_res < 0 || source_pcm_res >= dca_bits_per_sample.Length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int subframes = buffer.ReadBits(4);
|
||||
int total_channels = buffer.ReadBits(3) + 1 + ext_coding;
|
||||
|
||||
stream.SampleRate = dca_sample_rates[sample_rate];
|
||||
stream.ChannelCount = total_channels;
|
||||
stream.LFE = (lfe > 0 ? 1 : 0);
|
||||
stream.BitDepth = dca_bits_per_sample[source_pcm_res];
|
||||
stream.DialNorm = -dialog_norm;
|
||||
if ((source_pcm_res & 0x1) == 0x1)
|
||||
{
|
||||
stream.AudioMode = TSAudioMode.Extended;
|
||||
}
|
||||
|
||||
stream.BitRate = (uint)dca_bit_rates[bit_rate];
|
||||
switch (stream.BitRate)
|
||||
{
|
||||
case 1:
|
||||
if (bitrate > 0)
|
||||
{
|
||||
stream.BitRate = bitrate;
|
||||
stream.IsVBR = false;
|
||||
stream.IsInitialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.BitRate = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
stream.IsVBR = true;
|
||||
stream.IsInitialized = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
stream.IsVBR = false;
|
||||
stream.IsInitialized = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
249
BDInfo/TSCodecDTSHD.cs
Normal file
249
BDInfo/TSCodecDTSHD.cs
Normal file
@ -0,0 +1,249 @@
|
||||
//============================================================================
|
||||
// BDInfo - Blu-ray Video and Audio Analysis Tool
|
||||
// Copyright © 2010 Cinema Squid
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//=============================================================================
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BDInfo
|
||||
{
|
||||
public abstract class TSCodecDTSHD
|
||||
{
|
||||
private static int[] SampleRates = new int[]
|
||||
{ 0x1F40, 0x3E80, 0x7D00, 0x0FA00, 0x1F400, 0x5622, 0x0AC44, 0x15888, 0x2B110, 0x56220, 0x2EE0, 0x5DC0, 0x0BB80, 0x17700, 0x2EE00, 0x5DC00 };
|
||||
|
||||
public static void Scan(
|
||||
TSAudioStream stream,
|
||||
TSStreamBuffer buffer,
|
||||
long bitrate,
|
||||
ref string tag)
|
||||
{
|
||||
if (stream.IsInitialized &&
|
||||
(stream.StreamType == TSStreamType.DTS_HD_SECONDARY_AUDIO ||
|
||||
(stream.CoreStream != null &&
|
||||
stream.CoreStream.IsInitialized))) return;
|
||||
|
||||
bool syncFound = false;
|
||||
uint sync = 0;
|
||||
for (int i = 0; i < buffer.Length; i++)
|
||||
{
|
||||
sync = (sync << 8) + buffer.ReadByte();
|
||||
if (sync == 0x64582025)
|
||||
{
|
||||
syncFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!syncFound)
|
||||
{
|
||||
tag = "CORE";
|
||||
if (stream.CoreStream == null)
|
||||
{
|
||||
stream.CoreStream = new TSAudioStream();
|
||||
stream.CoreStream.StreamType = TSStreamType.DTS_AUDIO;
|
||||
}
|
||||
if (!stream.CoreStream.IsInitialized)
|
||||
{
|
||||
buffer.BeginRead();
|
||||
TSCodecDTS.Scan(stream.CoreStream, buffer, bitrate, ref tag);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
tag = "HD";
|
||||
int temp1 = buffer.ReadBits(8);
|
||||
int nuSubStreamIndex = buffer.ReadBits(2);
|
||||
int nuExtSSHeaderSize = 0;
|
||||
int nuExtSSFSize = 0;
|
||||
int bBlownUpHeader = buffer.ReadBits(1);
|
||||
if (1 == bBlownUpHeader)
|
||||
{
|
||||
nuExtSSHeaderSize = buffer.ReadBits(12) + 1;
|
||||
nuExtSSFSize = buffer.ReadBits(20) + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
nuExtSSHeaderSize = buffer.ReadBits(8) + 1;
|
||||
nuExtSSFSize = buffer.ReadBits(16) + 1;
|
||||
}
|
||||
int nuNumAudioPresent = 1;
|
||||
int nuNumAssets = 1;
|
||||
int bStaticFieldsPresent = buffer.ReadBits(1);
|
||||
if (1 == bStaticFieldsPresent)
|
||||
{
|
||||
int nuRefClockCode = buffer.ReadBits(2);
|
||||
int nuExSSFrameDurationCode = buffer.ReadBits(3) + 1;
|
||||
long nuTimeStamp = 0;
|
||||
if (1 == buffer.ReadBits(1))
|
||||
{
|
||||
nuTimeStamp = (buffer.ReadBits(18) << 18) + buffer.ReadBits(18);
|
||||
}
|
||||
nuNumAudioPresent = buffer.ReadBits(3) + 1;
|
||||
nuNumAssets = buffer.ReadBits(3) + 1;
|
||||
int[] nuActiveExSSMask = new int[nuNumAudioPresent];
|
||||
for (int i = 0; i < nuNumAudioPresent; i++)
|
||||
{
|
||||
nuActiveExSSMask[i] = buffer.ReadBits(nuSubStreamIndex + 1); //?
|
||||
}
|
||||
for (int i = 0; i < nuNumAudioPresent; i++)
|
||||
{
|
||||
for (int j = 0; j < nuSubStreamIndex + 1; j++)
|
||||
{
|
||||
if (((j + 1) % 2) == 1)
|
||||
{
|
||||
int mask = buffer.ReadBits(8);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (1 == buffer.ReadBits(1))
|
||||
{
|
||||
int nuMixMetadataAdjLevel = buffer.ReadBits(2);
|
||||
int nuBits4MixOutMask = buffer.ReadBits(2) * 4 + 4;
|
||||
int nuNumMixOutConfigs = buffer.ReadBits(2) + 1;
|
||||
int[] nuMixOutChMask = new int[nuNumMixOutConfigs];
|
||||
for (int i = 0; i < nuNumMixOutConfigs; i++)
|
||||
{
|
||||
nuMixOutChMask[i] = buffer.ReadBits(nuBits4MixOutMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
int[] AssetSizes = new int[nuNumAssets];
|
||||
for (int i = 0; i < nuNumAssets; i++)
|
||||
{
|
||||
if (1 == bBlownUpHeader)
|
||||
{
|
||||
AssetSizes[i] = buffer.ReadBits(20) + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
AssetSizes[i] = buffer.ReadBits(16) + 1;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < nuNumAssets; i++)
|
||||
{
|
||||
long bufferPosition = buffer.Position;
|
||||
int nuAssetDescriptorFSIZE = buffer.ReadBits(9) + 1;
|
||||
int DescriptorDataForAssetIndex = buffer.ReadBits(3);
|
||||
if (1 == bStaticFieldsPresent)
|
||||
{
|
||||
int AssetTypeDescrPresent = buffer.ReadBits(1);
|
||||
if (1 == AssetTypeDescrPresent)
|
||||
{
|
||||
int AssetTypeDescriptor = buffer.ReadBits(4);
|
||||
}
|
||||
int LanguageDescrPresent = buffer.ReadBits(1);
|
||||
if (1 == LanguageDescrPresent)
|
||||
{
|
||||
int LanguageDescriptor = buffer.ReadBits(24);
|
||||
}
|
||||
int bInfoTextPresent = buffer.ReadBits(1);
|
||||
if (1 == bInfoTextPresent)
|
||||
{
|
||||
int nuInfoTextByteSize = buffer.ReadBits(10) + 1;
|
||||
int[] InfoText = new int[nuInfoTextByteSize];
|
||||
for (int j = 0; j < nuInfoTextByteSize; j++)
|
||||
{
|
||||
InfoText[j] = buffer.ReadBits(8);
|
||||
}
|
||||
}
|
||||
int nuBitResolution = buffer.ReadBits(5) + 1;
|
||||
int nuMaxSampleRate = buffer.ReadBits(4);
|
||||
int nuTotalNumChs = buffer.ReadBits(8) + 1;
|
||||
int bOne2OneMapChannels2Speakers = buffer.ReadBits(1);
|
||||
int nuSpkrActivityMask = 0;
|
||||
if (1 == bOne2OneMapChannels2Speakers)
|
||||
{
|
||||
int bEmbeddedStereoFlag = 0;
|
||||
if (nuTotalNumChs > 2)
|
||||
{
|
||||
bEmbeddedStereoFlag = buffer.ReadBits(1);
|
||||
}
|
||||
int bEmbeddedSixChFlag = 0;
|
||||
if (nuTotalNumChs > 6)
|
||||
{
|
||||
bEmbeddedSixChFlag = buffer.ReadBits(1);
|
||||
}
|
||||
int bSpkrMaskEnabled = buffer.ReadBits(1);
|
||||
int nuNumBits4SAMask = 0;
|
||||
if (1 == bSpkrMaskEnabled)
|
||||
{
|
||||
nuNumBits4SAMask = buffer.ReadBits(2);
|
||||
nuNumBits4SAMask = nuNumBits4SAMask * 4 + 4;
|
||||
nuSpkrActivityMask = buffer.ReadBits(nuNumBits4SAMask);
|
||||
}
|
||||
// TODO...
|
||||
}
|
||||
stream.SampleRate = SampleRates[nuMaxSampleRate];
|
||||
stream.BitDepth = nuBitResolution;
|
||||
|
||||
stream.LFE = 0;
|
||||
if ((nuSpkrActivityMask & 0x8) == 0x8)
|
||||
{
|
||||
++stream.LFE;
|
||||
}
|
||||
if ((nuSpkrActivityMask & 0x1000) == 0x1000)
|
||||
{
|
||||
++stream.LFE;
|
||||
}
|
||||
stream.ChannelCount = nuTotalNumChs - stream.LFE;
|
||||
}
|
||||
if (nuNumAssets > 1)
|
||||
{
|
||||
// TODO...
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
if (stream.CoreStream != null)
|
||||
{
|
||||
TSAudioStream coreStream = (TSAudioStream)stream.CoreStream;
|
||||
if (coreStream.AudioMode == TSAudioMode.Extended &&
|
||||
stream.ChannelCount == 5)
|
||||
{
|
||||
stream.AudioMode = TSAudioMode.Extended;
|
||||
}
|
||||
/*
|
||||
if (coreStream.DialNorm != 0)
|
||||
{
|
||||
stream.DialNorm = coreStream.DialNorm;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
if (stream.StreamType == TSStreamType.DTS_HD_MASTER_AUDIO)
|
||||
{
|
||||
stream.IsVBR = true;
|
||||
stream.IsInitialized = true;
|
||||
}
|
||||
else if (bitrate > 0)
|
||||
{
|
||||
stream.IsVBR = false;
|
||||
stream.BitRate = bitrate;
|
||||
if (stream.CoreStream != null)
|
||||
{
|
||||
stream.BitRate += stream.CoreStream.BitRate;
|
||||
stream.IsInitialized = true;
|
||||
}
|
||||
stream.IsInitialized = (stream.BitRate > 0 ? true : false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
126
BDInfo/TSCodecLPCM.cs
Normal file
126
BDInfo/TSCodecLPCM.cs
Normal file
@ -0,0 +1,126 @@
|
||||
//============================================================================
|
||||
// BDInfo - Blu-ray Video and Audio Analysis Tool
|
||||
// Copyright © 2010 Cinema Squid
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//=============================================================================
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BDInfo
|
||||
{
|
||||
public abstract class TSCodecLPCM
|
||||
{
|
||||
public static void Scan(
|
||||
TSAudioStream stream,
|
||||
TSStreamBuffer buffer,
|
||||
ref string tag)
|
||||
{
|
||||
if (stream.IsInitialized) return;
|
||||
|
||||
byte[] header = buffer.ReadBytes(4);
|
||||
int flags = (header[2] << 8) + header[3];
|
||||
|
||||
switch ((flags & 0xF000) >> 12)
|
||||
{
|
||||
case 1: // 1/0/0
|
||||
stream.ChannelCount = 1;
|
||||
stream.LFE = 0;
|
||||
break;
|
||||
case 3: // 2/0/0
|
||||
stream.ChannelCount = 2;
|
||||
stream.LFE = 0;
|
||||
break;
|
||||
case 4: // 3/0/0
|
||||
stream.ChannelCount = 3;
|
||||
stream.LFE = 0;
|
||||
break;
|
||||
case 5: // 2/1/0
|
||||
stream.ChannelCount = 3;
|
||||
stream.LFE = 0;
|
||||
break;
|
||||
case 6: // 3/1/0
|
||||
stream.ChannelCount = 4;
|
||||
stream.LFE = 0;
|
||||
break;
|
||||
case 7: // 2/2/0
|
||||
stream.ChannelCount = 4;
|
||||
stream.LFE = 0;
|
||||
break;
|
||||
case 8: // 3/2/0
|
||||
stream.ChannelCount = 5;
|
||||
stream.LFE = 0;
|
||||
break;
|
||||
case 9: // 3/2/1
|
||||
stream.ChannelCount = 5;
|
||||
stream.LFE = 1;
|
||||
break;
|
||||
case 10: // 3/4/0
|
||||
stream.ChannelCount = 7;
|
||||
stream.LFE = 0;
|
||||
break;
|
||||
case 11: // 3/4/1
|
||||
stream.ChannelCount = 7;
|
||||
stream.LFE = 1;
|
||||
break;
|
||||
default:
|
||||
stream.ChannelCount = 0;
|
||||
stream.LFE = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
switch ((flags & 0xC0) >> 6)
|
||||
{
|
||||
case 1:
|
||||
stream.BitDepth = 16;
|
||||
break;
|
||||
case 2:
|
||||
stream.BitDepth = 20;
|
||||
break;
|
||||
case 3:
|
||||
stream.BitDepth = 24;
|
||||
break;
|
||||
default:
|
||||
stream.BitDepth = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
switch ((flags & 0xF00) >> 8)
|
||||
{
|
||||
case 1:
|
||||
stream.SampleRate = 48000;
|
||||
break;
|
||||
case 4:
|
||||
stream.SampleRate = 96000;
|
||||
break;
|
||||
case 5:
|
||||
stream.SampleRate = 192000;
|
||||
break;
|
||||
default:
|
||||
stream.SampleRate = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
stream.BitRate = (uint)
|
||||
(stream.SampleRate * stream.BitDepth *
|
||||
(stream.ChannelCount + stream.LFE));
|
||||
|
||||
stream.IsVBR = false;
|
||||
stream.IsInitialized = true;
|
||||
}
|
||||
}
|
||||
}
|
211
BDInfo/TSCodecMPEG2.cs
Normal file
211
BDInfo/TSCodecMPEG2.cs
Normal file
@ -0,0 +1,211 @@
|
||||
//============================================================================
|
||||
// BDInfo - Blu-ray Video and Audio Analysis Tool
|
||||
// Copyright © 2010 Cinema Squid
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//=============================================================================
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BDInfo
|
||||
{
|
||||
public abstract class TSCodecMPEG2
|
||||
{
|
||||
public static void Scan(
|
||||
TSVideoStream stream,
|
||||
TSStreamBuffer buffer,
|
||||
ref string tag)
|
||||
{
|
||||
int parse = 0;
|
||||
int pictureParse = 0;
|
||||
int sequenceHeaderParse = 0;
|
||||
int extensionParse = 0;
|
||||
int sequenceExtensionParse = 0;
|
||||
|
||||
for (int i = 0; i < buffer.Length; i++)
|
||||
{
|
||||
parse = (parse << 8) + buffer.ReadByte();
|
||||
|
||||
if (parse == 0x00000100)
|
||||
{
|
||||
pictureParse = 2;
|
||||
}
|
||||
else if (parse == 0x000001B3)
|
||||
{
|
||||
sequenceHeaderParse = 7;
|
||||
}
|
||||
else if (sequenceHeaderParse > 0)
|
||||
{
|
||||
--sequenceHeaderParse;
|
||||
switch (sequenceHeaderParse)
|
||||
{
|
||||
#if DEBUG
|
||||
case 6:
|
||||
break;
|
||||
|
||||
case 5:
|
||||
break;
|
||||
|
||||
case 4:
|
||||
stream.Width =
|
||||
(int)((parse & 0xFFF000) >> 12);
|
||||
stream.Height =
|
||||
(int)(parse & 0xFFF);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
stream.AspectRatio =
|
||||
(TSAspectRatio)((parse & 0xF0) >> 4);
|
||||
|
||||
switch ((parse & 0xF0) >> 4)
|
||||
{
|
||||
case 0: // Forbidden
|
||||
break;
|
||||
case 1: // Square
|
||||
break;
|
||||
case 2: // 4:3
|
||||
break;
|
||||
case 3: // 16:9
|
||||
break;
|
||||
case 4: // 2.21:1
|
||||
break;
|
||||
default: // Reserved
|
||||
break;
|
||||
}
|
||||
|
||||
switch (parse & 0xF)
|
||||
{
|
||||
case 0: // Forbidden
|
||||
break;
|
||||
case 1: // 23.976
|
||||
stream.FrameRateEnumerator = 24000;
|
||||
stream.FrameRateDenominator = 1001;
|
||||
break;
|
||||
case 2: // 24
|
||||
stream.FrameRateEnumerator = 24000;
|
||||
stream.FrameRateDenominator = 1000;
|
||||
break;
|
||||
case 3: // 25
|
||||
stream.FrameRateEnumerator = 25000;
|
||||
stream.FrameRateDenominator = 1000;
|
||||
break;
|
||||
case 4: // 29.97
|
||||
stream.FrameRateEnumerator = 30000;
|
||||
stream.FrameRateDenominator = 1001;
|
||||
break;
|
||||
case 5: // 30
|
||||
stream.FrameRateEnumerator = 30000;
|
||||
stream.FrameRateDenominator = 1000;
|
||||
break;
|
||||
case 6: // 50
|
||||
stream.FrameRateEnumerator = 50000;
|
||||
stream.FrameRateDenominator = 1000;
|
||||
break;
|
||||
case 7: // 59.94
|
||||
stream.FrameRateEnumerator = 60000;
|
||||
stream.FrameRateDenominator = 1001;
|
||||
break;
|
||||
case 8: // 60
|
||||
stream.FrameRateEnumerator = 60000;
|
||||
stream.FrameRateDenominator = 1000;
|
||||
break;
|
||||
default: // Reserved
|
||||
stream.FrameRateEnumerator = 0;
|
||||
stream.FrameRateDenominator = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
break;
|
||||
|
||||
case 1:
|
||||
break;
|
||||
#endif
|
||||
|
||||
case 0:
|
||||
#if DEBUG
|
||||
stream.BitRate =
|
||||
(((parse & 0xFFFFC0) >> 6) * 200);
|
||||
#endif
|
||||
stream.IsVBR = true;
|
||||
stream.IsInitialized = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (pictureParse > 0)
|
||||
{
|
||||
--pictureParse;
|
||||
if (pictureParse == 0)
|
||||
{
|
||||
switch ((parse & 0x38) >> 3)
|
||||
{
|
||||
case 1:
|
||||
tag = "I";
|
||||
break;
|
||||
case 2:
|
||||
tag = "P";
|
||||
break;
|
||||
case 3:
|
||||
tag = "B";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (stream.IsInitialized) return;
|
||||
}
|
||||
}
|
||||
else if (parse == 0x000001B5)
|
||||
{
|
||||
extensionParse = 1;
|
||||
}
|
||||
else if (extensionParse > 0)
|
||||
{
|
||||
--extensionParse;
|
||||
if (extensionParse == 0)
|
||||
{
|
||||
if ((parse & 0xF0) == 0x10)
|
||||
{
|
||||
sequenceExtensionParse = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sequenceExtensionParse > 0)
|
||||
{
|
||||
--sequenceExtensionParse;
|
||||
#if DEBUG
|
||||
if (sequenceExtensionParse == 0)
|
||||
{
|
||||
uint sequenceExtension =
|
||||
((parse & 0x8) >> 3);
|
||||
if (sequenceExtension == 0)
|
||||
{
|
||||
stream.IsInterlaced = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.IsInterlaced = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
39
BDInfo/TSCodecMVC.cs
Normal file
39
BDInfo/TSCodecMVC.cs
Normal file
@ -0,0 +1,39 @@
|
||||
//============================================================================
|
||||
// BDInfo - Blu-ray Video and Audio Analysis Tool
|
||||
// Copyright © 2010 Cinema Squid
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//=============================================================================
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BDInfo
|
||||
{
|
||||
// TODO: Do something more interesting here...
|
||||
|
||||
public abstract class TSCodecMVC
|
||||
{
|
||||
public static void Scan(
|
||||
TSVideoStream stream,
|
||||
TSStreamBuffer buffer,
|
||||
ref string tag)
|
||||
{
|
||||
stream.IsVBR = true;
|
||||
stream.IsInitialized = true;
|
||||
}
|
||||
}
|
||||
}
|
189
BDInfo/TSCodecTrueHD.cs
Normal file
189
BDInfo/TSCodecTrueHD.cs
Normal file
@ -0,0 +1,189 @@
|
||||
//============================================================================
|
||||
// BDInfo - Blu-ray Video and Audio Analysis Tool
|
||||
// Copyright © 2010 Cinema Squid
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//=============================================================================
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BDInfo
|
||||
{
|
||||
public abstract class TSCodecTrueHD
|
||||
{
|
||||
public static void Scan(
|
||||
TSAudioStream stream,
|
||||
TSStreamBuffer buffer,
|
||||
ref string tag)
|
||||
{
|
||||
if (stream.IsInitialized &&
|
||||
stream.CoreStream != null &&
|
||||
stream.CoreStream.IsInitialized) return;
|
||||
|
||||
bool syncFound = false;
|
||||
uint sync = 0;
|
||||
for (int i = 0; i < buffer.Length; i++)
|
||||
{
|
||||
sync = (sync << 8) + buffer.ReadByte();
|
||||
if (sync == 0xF8726FBA)
|
||||
{
|
||||
syncFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!syncFound)
|
||||
{
|
||||
tag = "CORE";
|
||||
if (stream.CoreStream == null)
|
||||
{
|
||||
stream.CoreStream = new TSAudioStream();
|
||||
stream.CoreStream.StreamType = TSStreamType.AC3_AUDIO;
|
||||
}
|
||||
if (!stream.CoreStream.IsInitialized)
|
||||
{
|
||||
buffer.BeginRead();
|
||||
TSCodecAC3.Scan(stream.CoreStream, buffer, ref tag);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
tag = "HD";
|
||||
int ratebits = buffer.ReadBits(4);
|
||||
if (ratebits != 0xF)
|
||||
{
|
||||
stream.SampleRate =
|
||||
(((ratebits & 8) > 0 ? 44100 : 48000) << (ratebits & 7));
|
||||
}
|
||||
int temp1 = buffer.ReadBits(8);
|
||||
int channels_thd_stream1 = buffer.ReadBits(5);
|
||||
int temp2 = buffer.ReadBits(2);
|
||||
|
||||
stream.ChannelCount = 0;
|
||||
stream.LFE = 0;
|
||||
int c_LFE2 = buffer.ReadBits(1);
|
||||
if (c_LFE2 == 1)
|
||||
{
|
||||
stream.LFE += 1;
|
||||
}
|
||||
int c_Cvh = buffer.ReadBits(1);
|
||||
if (c_Cvh == 1)
|
||||
{
|
||||
stream.ChannelCount += 1;
|
||||
}
|
||||
int c_LRw = buffer.ReadBits(1);
|
||||
if (c_LRw == 1)
|
||||
{
|
||||
stream.ChannelCount += 2;
|
||||
}
|
||||
int c_LRsd = buffer.ReadBits(1);
|
||||
if (c_LRsd == 1)
|
||||
{
|
||||
stream.ChannelCount += 2;
|
||||
}
|
||||
int c_Ts = buffer.ReadBits(1);
|
||||
if (c_Ts == 1)
|
||||
{
|
||||
stream.ChannelCount += 1;
|
||||
}
|
||||
int c_Cs = buffer.ReadBits(1);
|
||||
if (c_Cs == 1)
|
||||
{
|
||||
stream.ChannelCount += 1;
|
||||
}
|
||||
int c_LRrs = buffer.ReadBits(1);
|
||||
if (c_LRrs == 1)
|
||||
{
|
||||
stream.ChannelCount += 2;
|
||||
}
|
||||
int c_LRc = buffer.ReadBits(1);
|
||||
if (c_LRc == 1)
|
||||
{
|
||||
stream.ChannelCount += 2;
|
||||
}
|
||||
int c_LRvh = buffer.ReadBits(1);
|
||||
if (c_LRvh == 1)
|
||||
{
|
||||
stream.ChannelCount += 2;
|
||||
}
|
||||
int c_LRs = buffer.ReadBits(1);
|
||||
if (c_LRs == 1)
|
||||
{
|
||||
stream.ChannelCount += 2;
|
||||
}
|
||||
int c_LFE = buffer.ReadBits(1);
|
||||
if (c_LFE == 1)
|
||||
{
|
||||
stream.LFE += 1;
|
||||
}
|
||||
int c_C = buffer.ReadBits(1);
|
||||
if (c_C == 1)
|
||||
{
|
||||
stream.ChannelCount += 1;
|
||||
}
|
||||
int c_LR = buffer.ReadBits(1);
|
||||
if (c_LR == 1)
|
||||
{
|
||||
stream.ChannelCount += 2;
|
||||
}
|
||||
|
||||
int access_unit_size = 40 << (ratebits & 7);
|
||||
int access_unit_size_pow2 = 64 << (ratebits & 7);
|
||||
|
||||
int a1 = buffer.ReadBits(16);
|
||||
int a2 = buffer.ReadBits(16);
|
||||
int a3 = buffer.ReadBits(16);
|
||||
|
||||
int is_vbr = buffer.ReadBits(1);
|
||||
int peak_bitrate = buffer.ReadBits(15);
|
||||
peak_bitrate = (peak_bitrate * stream.SampleRate) >> 4;
|
||||
|
||||
double peak_bitdepth =
|
||||
(double)peak_bitrate /
|
||||
(stream.ChannelCount + stream.LFE) /
|
||||
stream.SampleRate;
|
||||
if (peak_bitdepth > 14)
|
||||
{
|
||||
stream.BitDepth = 24;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.BitDepth = 16;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
System.Diagnostics.Debug.WriteLine(string.Format(
|
||||
"{0}\t{1}\t{2:F2}",
|
||||
stream.PID, peak_bitrate, peak_bitdepth));
|
||||
#endif
|
||||
/*
|
||||
// TODO: Get THD dialnorm from metadata
|
||||
if (stream.CoreStream != null)
|
||||
{
|
||||
TSAudioStream coreStream = (TSAudioStream)stream.CoreStream;
|
||||
if (coreStream.DialNorm != 0)
|
||||
{
|
||||
stream.DialNorm = coreStream.DialNorm;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
stream.IsVBR = true;
|
||||
stream.IsInitialized = true;
|
||||
}
|
||||
}
|
||||
}
|
134
BDInfo/TSCodecVC1.cs
Normal file
134
BDInfo/TSCodecVC1.cs
Normal file
@ -0,0 +1,134 @@
|
||||
//============================================================================
|
||||
// BDInfo - Blu-ray Video and Audio Analysis Tool
|
||||
// Copyright © 2010 Cinema Squid
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//=============================================================================
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BDInfo
|
||||
{
|
||||
public abstract class TSCodecVC1
|
||||
{
|
||||
public static void Scan(
|
||||
TSVideoStream stream,
|
||||
TSStreamBuffer buffer,
|
||||
ref string tag)
|
||||
{
|
||||
int parse = 0;
|
||||
byte frameHeaderParse = 0;
|
||||
byte sequenceHeaderParse = 0;
|
||||
bool isInterlaced = false;
|
||||
|
||||
for (int i = 0; i < buffer.Length; i++)
|
||||
{
|
||||
parse = (parse << 8) + buffer.ReadByte();
|
||||
|
||||
if (parse == 0x0000010D)
|
||||
{
|
||||
frameHeaderParse = 4;
|
||||
}
|
||||
else if (frameHeaderParse > 0)
|
||||
{
|
||||
--frameHeaderParse;
|
||||
if (frameHeaderParse == 0)
|
||||
{
|
||||
uint pictureType = 0;
|
||||
if (isInterlaced)
|
||||
{
|
||||
if ((parse & 0x80000000) == 0)
|
||||
{
|
||||
pictureType =
|
||||
(uint)((parse & 0x78000000) >> 13);
|
||||
}
|
||||
else
|
||||
{
|
||||
pictureType =
|
||||
(uint)((parse & 0x3c000000) >> 12);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pictureType =
|
||||
(uint)((parse & 0xf0000000) >> 14);
|
||||
}
|
||||
|
||||
if ((pictureType & 0x20000) == 0)
|
||||
{
|
||||
tag = "P";
|
||||
}
|
||||
else if ((pictureType & 0x10000) == 0)
|
||||
{
|
||||
tag = "B";
|
||||
}
|
||||
else if ((pictureType & 0x8000) == 0)
|
||||
{
|
||||
tag = "I";
|
||||
}
|
||||
else if ((pictureType & 0x4000) == 0)
|
||||
{
|
||||
tag = "BI";
|
||||
}
|
||||
else
|
||||
{
|
||||
tag = null;
|
||||
}
|
||||
if (stream.IsInitialized) return;
|
||||
}
|
||||
}
|
||||
else if (parse == 0x0000010F)
|
||||
{
|
||||
sequenceHeaderParse = 6;
|
||||
}
|
||||
else if (sequenceHeaderParse > 0)
|
||||
{
|
||||
--sequenceHeaderParse;
|
||||
switch (sequenceHeaderParse)
|
||||
{
|
||||
case 5:
|
||||
int profileLevel = ((parse & 0x38) >> 3);
|
||||
if (((parse & 0xC0) >> 6) == 3)
|
||||
{
|
||||
stream.EncodingProfile = string.Format(
|
||||
"Advanced Profile {0}", profileLevel);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.EncodingProfile = string.Format(
|
||||
"Main Profile {0}", profileLevel);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0:
|
||||
if (((parse & 0x40) >> 6) > 0)
|
||||
{
|
||||
isInterlaced = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
isInterlaced = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
stream.IsVBR = true;
|
||||
stream.IsInitialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
40
BDInfo/TSInterleavedFile.cs
Normal file
40
BDInfo/TSInterleavedFile.cs
Normal file
@ -0,0 +1,40 @@
|
||||
//============================================================================
|
||||
// BDInfo - Blu-ray Video and Audio Analysis Tool
|
||||
// Copyright © 2010 Cinema Squid
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//=============================================================================
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
// TODO: Do more interesting things here...
|
||||
|
||||
namespace BDInfo
|
||||
{
|
||||
public class TSInterleavedFile
|
||||
{
|
||||
public FileInfo FileInfo = null;
|
||||
public string Name = null;
|
||||
|
||||
public TSInterleavedFile(FileInfo fileInfo)
|
||||
{
|
||||
FileInfo = fileInfo;
|
||||
Name = fileInfo.Name.ToUpper();
|
||||
}
|
||||
}
|
||||
}
|
1287
BDInfo/TSPlaylistFile.cs
Normal file
1287
BDInfo/TSPlaylistFile.cs
Normal file
File diff suppressed because it is too large
Load Diff
802
BDInfo/TSStream.cs
Normal file
802
BDInfo/TSStream.cs
Normal file
@ -0,0 +1,802 @@
|
||||
//============================================================================
|
||||
// BDInfo - Blu-ray Video and Audio Analysis Tool
|
||||
// Copyright © 2010 Cinema Squid
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//=============================================================================
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BDInfo
|
||||
{
|
||||
public enum TSStreamType : byte
|
||||
{
|
||||
Unknown = 0,
|
||||
MPEG1_VIDEO = 0x01,
|
||||
MPEG2_VIDEO = 0x02,
|
||||
AVC_VIDEO = 0x1b,
|
||||
MVC_VIDEO = 0x20,
|
||||
VC1_VIDEO = 0xea,
|
||||
MPEG1_AUDIO = 0x03,
|
||||
MPEG2_AUDIO = 0x04,
|
||||
LPCM_AUDIO = 0x80,
|
||||
AC3_AUDIO = 0x81,
|
||||
AC3_PLUS_AUDIO = 0x84,
|
||||
AC3_PLUS_SECONDARY_AUDIO = 0xA1,
|
||||
AC3_TRUE_HD_AUDIO = 0x83,
|
||||
DTS_AUDIO = 0x82,
|
||||
DTS_HD_AUDIO = 0x85,
|
||||
DTS_HD_SECONDARY_AUDIO = 0xA2,
|
||||
DTS_HD_MASTER_AUDIO = 0x86,
|
||||
PRESENTATION_GRAPHICS = 0x90,
|
||||
INTERACTIVE_GRAPHICS = 0x91,
|
||||
SUBTITLE = 0x92
|
||||
}
|
||||
|
||||
public enum TSVideoFormat : byte
|
||||
{
|
||||
Unknown = 0,
|
||||
VIDEOFORMAT_480i = 1,
|
||||
VIDEOFORMAT_576i = 2,
|
||||
VIDEOFORMAT_480p = 3,
|
||||
VIDEOFORMAT_1080i = 4,
|
||||
VIDEOFORMAT_720p = 5,
|
||||
VIDEOFORMAT_1080p = 6,
|
||||
VIDEOFORMAT_576p = 7,
|
||||
}
|
||||
|
||||
public enum TSFrameRate : byte
|
||||
{
|
||||
Unknown = 0,
|
||||
FRAMERATE_23_976 = 1,
|
||||
FRAMERATE_24 = 2,
|
||||
FRAMERATE_25 = 3,
|
||||
FRAMERATE_29_97 = 4,
|
||||
FRAMERATE_50 = 6,
|
||||
FRAMERATE_59_94 = 7
|
||||
}
|
||||
|
||||
public enum TSChannelLayout : byte
|
||||
{
|
||||
Unknown = 0,
|
||||
CHANNELLAYOUT_MONO = 1,
|
||||
CHANNELLAYOUT_STEREO = 3,
|
||||
CHANNELLAYOUT_MULTI = 6,
|
||||
CHANNELLAYOUT_COMBO = 12
|
||||
}
|
||||
|
||||
public enum TSSampleRate : byte
|
||||
{
|
||||
Unknown = 0,
|
||||
SAMPLERATE_48 = 1,
|
||||
SAMPLERATE_96 = 4,
|
||||
SAMPLERATE_192 = 5,
|
||||
SAMPLERATE_48_192 = 12,
|
||||
SAMPLERATE_48_96 = 14
|
||||
}
|
||||
|
||||
public enum TSAspectRatio
|
||||
{
|
||||
Unknown = 0,
|
||||
ASPECT_4_3 = 2,
|
||||
ASPECT_16_9 = 3,
|
||||
ASPECT_2_21 = 4
|
||||
}
|
||||
|
||||
public class TSDescriptor
|
||||
{
|
||||
public byte Name;
|
||||
public byte[] Value;
|
||||
|
||||
public TSDescriptor(byte name, byte length)
|
||||
{
|
||||
Name = name;
|
||||
Value = new byte[length];
|
||||
}
|
||||
|
||||
public TSDescriptor Clone()
|
||||
{
|
||||
TSDescriptor descriptor =
|
||||
new TSDescriptor(Name, (byte)Value.Length);
|
||||
Value.CopyTo(descriptor.Value, 0);
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class TSStream
|
||||
{
|
||||
public TSStream()
|
||||
{
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0} ({1})", CodecShortName, PID);
|
||||
}
|
||||
|
||||
public ushort PID;
|
||||
public TSStreamType StreamType;
|
||||
public List<TSDescriptor> Descriptors = null;
|
||||
public long BitRate = 0;
|
||||
public long ActiveBitRate = 0;
|
||||
public bool IsVBR = false;
|
||||
public bool IsInitialized = false;
|
||||
public string LanguageName;
|
||||
public bool IsHidden = false;
|
||||
|
||||
public ulong PayloadBytes = 0;
|
||||
public ulong PacketCount = 0;
|
||||
public double PacketSeconds = 0;
|
||||
public int AngleIndex = 0;
|
||||
|
||||
public ulong PacketSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return PacketCount * 192;
|
||||
}
|
||||
}
|
||||
|
||||
private string _LanguageCode;
|
||||
public string LanguageCode
|
||||
{
|
||||
get
|
||||
{
|
||||
return _LanguageCode;
|
||||
}
|
||||
set
|
||||
{
|
||||
_LanguageCode = value;
|
||||
LanguageName = LanguageCodes.GetName(value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsVideoStream
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (StreamType)
|
||||
{
|
||||
case TSStreamType.MPEG1_VIDEO:
|
||||
case TSStreamType.MPEG2_VIDEO:
|
||||
case TSStreamType.AVC_VIDEO:
|
||||
case TSStreamType.MVC_VIDEO:
|
||||
case TSStreamType.VC1_VIDEO:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsAudioStream
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (StreamType)
|
||||
{
|
||||
case TSStreamType.MPEG1_AUDIO:
|
||||
case TSStreamType.MPEG2_AUDIO:
|
||||
case TSStreamType.LPCM_AUDIO:
|
||||
case TSStreamType.AC3_AUDIO:
|
||||
case TSStreamType.AC3_PLUS_AUDIO:
|
||||
case TSStreamType.AC3_PLUS_SECONDARY_AUDIO:
|
||||
case TSStreamType.AC3_TRUE_HD_AUDIO:
|
||||
case TSStreamType.DTS_AUDIO:
|
||||
case TSStreamType.DTS_HD_AUDIO:
|
||||
case TSStreamType.DTS_HD_SECONDARY_AUDIO:
|
||||
case TSStreamType.DTS_HD_MASTER_AUDIO:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsGraphicsStream
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (StreamType)
|
||||
{
|
||||
case TSStreamType.PRESENTATION_GRAPHICS:
|
||||
case TSStreamType.INTERACTIVE_GRAPHICS:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsTextStream
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (StreamType)
|
||||
{
|
||||
case TSStreamType.SUBTITLE:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string CodecName
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (StreamType)
|
||||
{
|
||||
case TSStreamType.MPEG1_VIDEO:
|
||||
return "MPEG-1 Video";
|
||||
case TSStreamType.MPEG2_VIDEO:
|
||||
return "MPEG-2 Video";
|
||||
case TSStreamType.AVC_VIDEO:
|
||||
return "MPEG-4 AVC Video";
|
||||
case TSStreamType.MVC_VIDEO:
|
||||
return "MPEG-4 MVC Video";
|
||||
case TSStreamType.VC1_VIDEO:
|
||||
return "VC-1 Video";
|
||||
case TSStreamType.MPEG1_AUDIO:
|
||||
return "MP1 Audio";
|
||||
case TSStreamType.MPEG2_AUDIO:
|
||||
return "MP2 Audio";
|
||||
case TSStreamType.LPCM_AUDIO:
|
||||
return "LPCM Audio";
|
||||
case TSStreamType.AC3_AUDIO:
|
||||
if (((TSAudioStream)this).AudioMode == TSAudioMode.Extended)
|
||||
return "Dolby Digital EX Audio";
|
||||
else
|
||||
return "Dolby Digital Audio";
|
||||
case TSStreamType.AC3_PLUS_AUDIO:
|
||||
case TSStreamType.AC3_PLUS_SECONDARY_AUDIO:
|
||||
return "Dolby Digital Plus Audio";
|
||||
case TSStreamType.AC3_TRUE_HD_AUDIO:
|
||||
return "Dolby TrueHD Audio";
|
||||
case TSStreamType.DTS_AUDIO:
|
||||
if (((TSAudioStream)this).AudioMode == TSAudioMode.Extended)
|
||||
return "DTS-ES Audio";
|
||||
else
|
||||
return "DTS Audio";
|
||||
case TSStreamType.DTS_HD_AUDIO:
|
||||
return "DTS-HD High-Res Audio";
|
||||
case TSStreamType.DTS_HD_SECONDARY_AUDIO:
|
||||
return "DTS Express";
|
||||
case TSStreamType.DTS_HD_MASTER_AUDIO:
|
||||
return "DTS-HD Master Audio";
|
||||
case TSStreamType.PRESENTATION_GRAPHICS:
|
||||
return "Presentation Graphics";
|
||||
case TSStreamType.INTERACTIVE_GRAPHICS:
|
||||
return "Interactive Graphics";
|
||||
case TSStreamType.SUBTITLE:
|
||||
return "Subtitle";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string CodecAltName
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (StreamType)
|
||||
{
|
||||
case TSStreamType.MPEG1_VIDEO:
|
||||
return "MPEG-1";
|
||||
case TSStreamType.MPEG2_VIDEO:
|
||||
return "MPEG-2";
|
||||
case TSStreamType.AVC_VIDEO:
|
||||
return "AVC";
|
||||
case TSStreamType.MVC_VIDEO:
|
||||
return "MVC";
|
||||
case TSStreamType.VC1_VIDEO:
|
||||
return "VC-1";
|
||||
case TSStreamType.MPEG1_AUDIO:
|
||||
return "MP1";
|
||||
case TSStreamType.MPEG2_AUDIO:
|
||||
return "MP2";
|
||||
case TSStreamType.LPCM_AUDIO:
|
||||
return "LPCM";
|
||||
case TSStreamType.AC3_AUDIO:
|
||||
return "DD AC3";
|
||||
case TSStreamType.AC3_PLUS_AUDIO:
|
||||
case TSStreamType.AC3_PLUS_SECONDARY_AUDIO:
|
||||
return "DD AC3+";
|
||||
case TSStreamType.AC3_TRUE_HD_AUDIO:
|
||||
return "Dolby TrueHD";
|
||||
case TSStreamType.DTS_AUDIO:
|
||||
return "DTS";
|
||||
case TSStreamType.DTS_HD_AUDIO:
|
||||
return "DTS-HD Hi-Res";
|
||||
case TSStreamType.DTS_HD_SECONDARY_AUDIO:
|
||||
return "DTS Express";
|
||||
case TSStreamType.DTS_HD_MASTER_AUDIO:
|
||||
return "DTS-HD Master";
|
||||
case TSStreamType.PRESENTATION_GRAPHICS:
|
||||
return "PGS";
|
||||
case TSStreamType.INTERACTIVE_GRAPHICS:
|
||||
return "IGS";
|
||||
case TSStreamType.SUBTITLE:
|
||||
return "SUB";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string CodecShortName
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (StreamType)
|
||||
{
|
||||
case TSStreamType.MPEG1_VIDEO:
|
||||
return "MPEG-1";
|
||||
case TSStreamType.MPEG2_VIDEO:
|
||||
return "MPEG-2";
|
||||
case TSStreamType.AVC_VIDEO:
|
||||
return "AVC";
|
||||
case TSStreamType.MVC_VIDEO:
|
||||
return "MVC";
|
||||
case TSStreamType.VC1_VIDEO:
|
||||
return "VC-1";
|
||||
case TSStreamType.MPEG1_AUDIO:
|
||||
return "MP1";
|
||||
case TSStreamType.MPEG2_AUDIO:
|
||||
return "MP2";
|
||||
case TSStreamType.LPCM_AUDIO:
|
||||
return "LPCM";
|
||||
case TSStreamType.AC3_AUDIO:
|
||||
if (((TSAudioStream)this).AudioMode == TSAudioMode.Extended)
|
||||
return "AC3-EX";
|
||||
else
|
||||
return "AC3";
|
||||
case TSStreamType.AC3_PLUS_AUDIO:
|
||||
case TSStreamType.AC3_PLUS_SECONDARY_AUDIO:
|
||||
return "AC3+";
|
||||
case TSStreamType.AC3_TRUE_HD_AUDIO:
|
||||
return "TrueHD";
|
||||
case TSStreamType.DTS_AUDIO:
|
||||
if (((TSAudioStream)this).AudioMode == TSAudioMode.Extended)
|
||||
return "DTS-ES";
|
||||
else
|
||||
return "DTS";
|
||||
case TSStreamType.DTS_HD_AUDIO:
|
||||
return "DTS-HD HR";
|
||||
case TSStreamType.DTS_HD_SECONDARY_AUDIO:
|
||||
return "DTS Express";
|
||||
case TSStreamType.DTS_HD_MASTER_AUDIO:
|
||||
return "DTS-HD MA";
|
||||
case TSStreamType.PRESENTATION_GRAPHICS:
|
||||
return "PGS";
|
||||
case TSStreamType.INTERACTIVE_GRAPHICS:
|
||||
return "IGS";
|
||||
case TSStreamType.SUBTITLE:
|
||||
return "SUB";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string Description
|
||||
{
|
||||
get
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public abstract TSStream Clone();
|
||||
|
||||
protected void CopyTo(TSStream stream)
|
||||
{
|
||||
stream.PID = PID;
|
||||
stream.StreamType = StreamType;
|
||||
stream.IsVBR = IsVBR;
|
||||
stream.BitRate = BitRate;
|
||||
stream.IsInitialized = IsInitialized;
|
||||
stream.LanguageCode = _LanguageCode;
|
||||
if (Descriptors != null)
|
||||
{
|
||||
stream.Descriptors = new List<TSDescriptor>();
|
||||
foreach (TSDescriptor descriptor in Descriptors)
|
||||
{
|
||||
stream.Descriptors.Add(descriptor.Clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TSVideoStream : TSStream
|
||||
{
|
||||
public TSVideoStream()
|
||||
{
|
||||
}
|
||||
|
||||
public int Width;
|
||||
public int Height;
|
||||
public bool IsInterlaced;
|
||||
public int FrameRateEnumerator;
|
||||
public int FrameRateDenominator;
|
||||
public TSAspectRatio AspectRatio;
|
||||
public string EncodingProfile;
|
||||
|
||||
private TSVideoFormat _VideoFormat;
|
||||
public TSVideoFormat VideoFormat
|
||||
{
|
||||
get
|
||||
{
|
||||
return _VideoFormat;
|
||||
}
|
||||
set
|
||||
{
|
||||
_VideoFormat = value;
|
||||
switch (value)
|
||||
{
|
||||
case TSVideoFormat.VIDEOFORMAT_480i:
|
||||
Height = 480;
|
||||
IsInterlaced = true;
|
||||
break;
|
||||
case TSVideoFormat.VIDEOFORMAT_480p:
|
||||
Height = 480;
|
||||
IsInterlaced = false;
|
||||
break;
|
||||
case TSVideoFormat.VIDEOFORMAT_576i:
|
||||
Height = 576;
|
||||
IsInterlaced = true;
|
||||
break;
|
||||
case TSVideoFormat.VIDEOFORMAT_576p:
|
||||
Height = 576;
|
||||
IsInterlaced = false;
|
||||
break;
|
||||
case TSVideoFormat.VIDEOFORMAT_720p:
|
||||
Height = 720;
|
||||
IsInterlaced = false;
|
||||
break;
|
||||
case TSVideoFormat.VIDEOFORMAT_1080i:
|
||||
Height = 1080;
|
||||
IsInterlaced = true;
|
||||
break;
|
||||
case TSVideoFormat.VIDEOFORMAT_1080p:
|
||||
Height = 1080;
|
||||
IsInterlaced = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TSFrameRate _FrameRate;
|
||||
public TSFrameRate FrameRate
|
||||
{
|
||||
get
|
||||
{
|
||||
return _FrameRate;
|
||||
}
|
||||
set
|
||||
{
|
||||
_FrameRate = value;
|
||||
switch (value)
|
||||
{
|
||||
case TSFrameRate.FRAMERATE_23_976:
|
||||
FrameRateEnumerator = 24000;
|
||||
FrameRateDenominator = 1001;
|
||||
break;
|
||||
case TSFrameRate.FRAMERATE_24:
|
||||
FrameRateEnumerator = 24000;
|
||||
FrameRateDenominator = 1000;
|
||||
break;
|
||||
case TSFrameRate.FRAMERATE_25:
|
||||
FrameRateEnumerator = 25000;
|
||||
FrameRateDenominator = 1000;
|
||||
break;
|
||||
case TSFrameRate.FRAMERATE_29_97:
|
||||
FrameRateEnumerator = 30000;
|
||||
FrameRateDenominator = 1001;
|
||||
break;
|
||||
case TSFrameRate.FRAMERATE_50:
|
||||
FrameRateEnumerator = 50000;
|
||||
FrameRateDenominator = 1000;
|
||||
break;
|
||||
case TSFrameRate.FRAMERATE_59_94:
|
||||
FrameRateEnumerator = 60000;
|
||||
FrameRateDenominator = 1001;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string Description
|
||||
{
|
||||
get
|
||||
{
|
||||
string description = "";
|
||||
|
||||
if (Height > 0)
|
||||
{
|
||||
description += string.Format("{0:D}{1} / ",
|
||||
Height,
|
||||
IsInterlaced ? "i" : "p");
|
||||
}
|
||||
if (FrameRateEnumerator > 0 &&
|
||||
FrameRateDenominator > 0)
|
||||
{
|
||||
if (FrameRateEnumerator % FrameRateDenominator == 0)
|
||||
{
|
||||
description += string.Format("{0:D} fps / ",
|
||||
FrameRateEnumerator / FrameRateDenominator);
|
||||
}
|
||||
else
|
||||
{
|
||||
description += string.Format("{0:F3} fps / ",
|
||||
(double)FrameRateEnumerator / FrameRateDenominator);
|
||||
}
|
||||
|
||||
}
|
||||
if (AspectRatio == TSAspectRatio.ASPECT_4_3)
|
||||
{
|
||||
description += "4:3 / ";
|
||||
}
|
||||
else if (AspectRatio == TSAspectRatio.ASPECT_16_9)
|
||||
{
|
||||
description += "16:9 / ";
|
||||
}
|
||||
if (EncodingProfile != null)
|
||||
{
|
||||
description += EncodingProfile + " / ";
|
||||
}
|
||||
if (description.EndsWith(" / "))
|
||||
{
|
||||
description = description.Substring(0, description.Length - 3);
|
||||
}
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
public override TSStream Clone()
|
||||
{
|
||||
TSVideoStream stream = new TSVideoStream();
|
||||
CopyTo(stream);
|
||||
|
||||
stream.VideoFormat = _VideoFormat;
|
||||
stream.FrameRate = _FrameRate;
|
||||
stream.Width = Width;
|
||||
stream.Height = Height;
|
||||
stream.IsInterlaced = IsInterlaced;
|
||||
stream.FrameRateEnumerator = FrameRateEnumerator;
|
||||
stream.FrameRateDenominator = FrameRateDenominator;
|
||||
stream.AspectRatio = AspectRatio;
|
||||
stream.EncodingProfile = EncodingProfile;
|
||||
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
|
||||
public enum TSAudioMode
|
||||
{
|
||||
Unknown,
|
||||
DualMono,
|
||||
Stereo,
|
||||
Surround,
|
||||
Extended
|
||||
}
|
||||
|
||||
public class TSAudioStream : TSStream
|
||||
{
|
||||
public TSAudioStream()
|
||||
{
|
||||
}
|
||||
|
||||
public int SampleRate;
|
||||
public int ChannelCount;
|
||||
public int BitDepth;
|
||||
public int LFE;
|
||||
public int DialNorm;
|
||||
public TSAudioMode AudioMode;
|
||||
public TSAudioStream CoreStream;
|
||||
public TSChannelLayout ChannelLayout;
|
||||
|
||||
public static int ConvertSampleRate(
|
||||
TSSampleRate sampleRate)
|
||||
{
|
||||
switch (sampleRate)
|
||||
{
|
||||
case TSSampleRate.SAMPLERATE_48:
|
||||
return 48000;
|
||||
|
||||
case TSSampleRate.SAMPLERATE_96:
|
||||
case TSSampleRate.SAMPLERATE_48_96:
|
||||
return 96000;
|
||||
|
||||
case TSSampleRate.SAMPLERATE_192:
|
||||
case TSSampleRate.SAMPLERATE_48_192:
|
||||
return 192000;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public string ChannelDescription
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ChannelLayout == TSChannelLayout.CHANNELLAYOUT_MONO &&
|
||||
ChannelCount == 2)
|
||||
{
|
||||
}
|
||||
|
||||
string description = "";
|
||||
if (ChannelCount > 0)
|
||||
{
|
||||
description += string.Format(
|
||||
"{0:D}.{1:D}",
|
||||
ChannelCount, LFE);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (ChannelLayout)
|
||||
{
|
||||
case TSChannelLayout.CHANNELLAYOUT_MONO:
|
||||
description += "1.0";
|
||||
break;
|
||||
case TSChannelLayout.CHANNELLAYOUT_STEREO:
|
||||
description += "2.0";
|
||||
break;
|
||||
case TSChannelLayout.CHANNELLAYOUT_MULTI:
|
||||
description += "5.1";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (AudioMode == TSAudioMode.Extended)
|
||||
{
|
||||
if (StreamType == TSStreamType.AC3_AUDIO)
|
||||
{
|
||||
description += "-EX";
|
||||
}
|
||||
if (StreamType == TSStreamType.DTS_AUDIO ||
|
||||
StreamType == TSStreamType.DTS_HD_AUDIO ||
|
||||
StreamType == TSStreamType.DTS_HD_MASTER_AUDIO)
|
||||
{
|
||||
description += "-ES";
|
||||
}
|
||||
}
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
public override string Description
|
||||
{
|
||||
get
|
||||
{
|
||||
string description = ChannelDescription;
|
||||
|
||||
if (SampleRate > 0)
|
||||
{
|
||||
description += string.Format(
|
||||
" / {0:D} kHz", SampleRate / 1000);
|
||||
}
|
||||
if (BitRate > 0)
|
||||
{
|
||||
description += string.Format(
|
||||
" / {0:D} kbps", (uint)Math.Round((double)BitRate / 1000));
|
||||
}
|
||||
if (BitDepth > 0)
|
||||
{
|
||||
description += string.Format(
|
||||
" / {0:D}-bit", BitDepth);
|
||||
}
|
||||
if (DialNorm != 0)
|
||||
{
|
||||
description += string.Format(
|
||||
" / DN {0}dB", DialNorm);
|
||||
}
|
||||
if (ChannelCount == 2)
|
||||
{
|
||||
switch (AudioMode)
|
||||
{
|
||||
case TSAudioMode.DualMono:
|
||||
description += " / Dual Mono";
|
||||
break;
|
||||
|
||||
case TSAudioMode.Surround:
|
||||
description += " / Dolby Surround";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (description.EndsWith(" / "))
|
||||
{
|
||||
description = description.Substring(0, description.Length - 3);
|
||||
}
|
||||
if (CoreStream != null)
|
||||
{
|
||||
string codec = "";
|
||||
switch (CoreStream.StreamType)
|
||||
{
|
||||
case TSStreamType.AC3_AUDIO:
|
||||
codec = "AC3 Embedded";
|
||||
break;
|
||||
case TSStreamType.DTS_AUDIO:
|
||||
codec = "DTS Core";
|
||||
break;
|
||||
}
|
||||
description += string.Format(
|
||||
" ({0}: {1})",
|
||||
codec,
|
||||
CoreStream.Description);
|
||||
}
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
public override TSStream Clone()
|
||||
{
|
||||
TSAudioStream stream = new TSAudioStream();
|
||||
CopyTo(stream);
|
||||
|
||||
stream.SampleRate = SampleRate;
|
||||
stream.ChannelLayout = ChannelLayout;
|
||||
stream.ChannelCount = ChannelCount;
|
||||
stream.BitDepth = BitDepth;
|
||||
stream.LFE = LFE;
|
||||
stream.DialNorm = DialNorm;
|
||||
stream.AudioMode = AudioMode;
|
||||
if (CoreStream != null)
|
||||
{
|
||||
stream.CoreStream = (TSAudioStream)CoreStream.Clone();
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
|
||||
public class TSGraphicsStream : TSStream
|
||||
{
|
||||
public TSGraphicsStream()
|
||||
{
|
||||
IsVBR = true;
|
||||
IsInitialized = true;
|
||||
}
|
||||
|
||||
public override TSStream Clone()
|
||||
{
|
||||
TSGraphicsStream stream = new TSGraphicsStream();
|
||||
CopyTo(stream);
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
|
||||
public class TSTextStream : TSStream
|
||||
{
|
||||
public TSTextStream()
|
||||
{
|
||||
IsVBR = true;
|
||||
IsInitialized = true;
|
||||
}
|
||||
|
||||
public override TSStream Clone()
|
||||
{
|
||||
TSTextStream stream = new TSTextStream();
|
||||
CopyTo(stream);
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
}
|
145
BDInfo/TSStreamBuffer.cs
Normal file
145
BDInfo/TSStreamBuffer.cs
Normal file
@ -0,0 +1,145 @@
|
||||
//============================================================================
|
||||
// BDInfo - Blu-ray Video and Audio Analysis Tool
|
||||
// Copyright © 2010 Cinema Squid
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//=============================================================================
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace BDInfo
|
||||
{
|
||||
public class TSStreamBuffer
|
||||
{
|
||||
private MemoryStream Stream = new MemoryStream();
|
||||
private int SkipBits = 0;
|
||||
private byte[] Buffer;
|
||||
private int BufferLength = 0;
|
||||
public int TransferLength = 0;
|
||||
|
||||
public TSStreamBuffer()
|
||||
{
|
||||
Buffer = new byte[4096];
|
||||
Stream = new MemoryStream(Buffer);
|
||||
}
|
||||
|
||||
public long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return (long)BufferLength;
|
||||
}
|
||||
}
|
||||
|
||||
public long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return Stream.Position;
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
int length)
|
||||
{
|
||||
TransferLength += length;
|
||||
|
||||
if (BufferLength + length >= Buffer.Length)
|
||||
{
|
||||
length = Buffer.Length - BufferLength;
|
||||
}
|
||||
if (length > 0)
|
||||
{
|
||||
Array.Copy(buffer, offset, Buffer, BufferLength, length);
|
||||
BufferLength += length;
|
||||
}
|
||||
}
|
||||
|
||||
public void Seek(
|
||||
long offset,
|
||||
SeekOrigin loc)
|
||||
{
|
||||
Stream.Seek(offset, loc);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
BufferLength = 0;
|
||||
TransferLength = 0;
|
||||
}
|
||||
|
||||
public void BeginRead()
|
||||
{
|
||||
SkipBits = 0;
|
||||
Stream.Seek(0, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
public void EndRead()
|
||||
{
|
||||
}
|
||||
|
||||
public byte[] ReadBytes(int bytes)
|
||||
{
|
||||
if (Stream.Position + bytes >= BufferLength)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] value = new byte[bytes];
|
||||
Stream.Read(value, 0, bytes);
|
||||
return value;
|
||||
}
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
return (byte)Stream.ReadByte();
|
||||
}
|
||||
|
||||
public int ReadBits(int bits)
|
||||
{
|
||||
long pos = Stream.Position;
|
||||
|
||||
int shift = 24;
|
||||
int data = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (pos + i >= BufferLength) break;
|
||||
data += (Stream.ReadByte() << shift);
|
||||
shift -= 8;
|
||||
}
|
||||
BitVector32 vector = new BitVector32(data);
|
||||
|
||||
int value = 0;
|
||||
for (int i = SkipBits; i < SkipBits + bits; i++)
|
||||
{
|
||||
value <<= 1;
|
||||
value += (vector[1 << (32 - i - 1)] ? 1 : 0);
|
||||
}
|
||||
|
||||
SkipBits += bits;
|
||||
Stream.Seek(pos + (SkipBits >> 3), SeekOrigin.Begin);
|
||||
SkipBits = SkipBits % 8;
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
114
BDInfo/TSStreamClip.cs
Normal file
114
BDInfo/TSStreamClip.cs
Normal file
@ -0,0 +1,114 @@
|
||||
//============================================================================
|
||||
// BDInfo - Blu-ray Video and Audio Analysis Tool
|
||||
// Copyright © 2010 Cinema Squid
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//=============================================================================
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BDInfo
|
||||
{
|
||||
public class TSStreamClip
|
||||
{
|
||||
public int AngleIndex = 0;
|
||||
public string Name;
|
||||
public double TimeIn;
|
||||
public double TimeOut;
|
||||
public double RelativeTimeIn;
|
||||
public double RelativeTimeOut;
|
||||
public double Length;
|
||||
|
||||
public ulong FileSize = 0;
|
||||
public ulong InterleavedFileSize = 0;
|
||||
public ulong PayloadBytes = 0;
|
||||
public ulong PacketCount = 0;
|
||||
public double PacketSeconds = 0;
|
||||
|
||||
public List<double> Chapters = new List<double>();
|
||||
|
||||
public TSStreamFile StreamFile = null;
|
||||
public TSStreamClipFile StreamClipFile = null;
|
||||
|
||||
public TSStreamClip(
|
||||
TSStreamFile streamFile,
|
||||
TSStreamClipFile streamClipFile)
|
||||
{
|
||||
if (streamFile != null)
|
||||
{
|
||||
Name = streamFile.Name;
|
||||
StreamFile = streamFile;
|
||||
FileSize = (ulong)StreamFile.FileInfo.Length;
|
||||
if (StreamFile.InterleavedFile != null)
|
||||
{
|
||||
InterleavedFileSize = (ulong)StreamFile.InterleavedFile.FileInfo.Length;
|
||||
}
|
||||
}
|
||||
StreamClipFile = streamClipFile;
|
||||
}
|
||||
|
||||
public string DisplayName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (StreamFile != null &&
|
||||
StreamFile.InterleavedFile != null &&
|
||||
BDInfoSettings.EnableSSIF)
|
||||
{
|
||||
return StreamFile.InterleavedFile.Name;
|
||||
}
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
||||
public ulong PacketSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return PacketCount * 192;
|
||||
}
|
||||
}
|
||||
|
||||
public ulong PacketBitRate
|
||||
{
|
||||
get
|
||||
{
|
||||
if (PacketSeconds > 0)
|
||||
{
|
||||
return (ulong)Math.Round(((PacketSize * 8.0) / PacketSeconds));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCompatible(TSStreamClip clip)
|
||||
{
|
||||
foreach (TSStream stream1 in StreamFile.Streams.Values)
|
||||
{
|
||||
if (clip.StreamFile.Streams.ContainsKey(stream1.PID))
|
||||
{
|
||||
TSStream stream2 = clip.StreamFile.Streams[stream1.PID];
|
||||
if (stream1.StreamType != stream2.StreamType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
248
BDInfo/TSStreamClipFile.cs
Normal file
248
BDInfo/TSStreamClipFile.cs
Normal file
@ -0,0 +1,248 @@
|
||||
//============================================================================
|
||||
// BDInfo - Blu-ray Video and Audio Analysis Tool
|
||||
// Copyright © 2010 Cinema Squid
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//=============================================================================
|
||||
|
||||
#undef DEBUG
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace BDInfo
|
||||
{
|
||||
public class TSStreamClipFile
|
||||
{
|
||||
public FileInfo FileInfo = null;
|
||||
public string FileType = null;
|
||||
public bool IsValid = false;
|
||||
public string Name = null;
|
||||
|
||||
public Dictionary<ushort, TSStream> Streams =
|
||||
new Dictionary<ushort,TSStream>();
|
||||
|
||||
public TSStreamClipFile(
|
||||
FileInfo fileInfo)
|
||||
{
|
||||
FileInfo = fileInfo;
|
||||
Name = fileInfo.Name.ToUpper();
|
||||
}
|
||||
|
||||
public void Scan()
|
||||
{
|
||||
FileStream fileStream = null;
|
||||
BinaryReader fileReader = null;
|
||||
|
||||
try
|
||||
{
|
||||
#if DEBUG
|
||||
Debug.WriteLine(string.Format(
|
||||
"Scanning {0}...", Name));
|
||||
#endif
|
||||
Streams.Clear();
|
||||
|
||||
fileStream = File.OpenRead(FileInfo.FullName);
|
||||
fileReader = new BinaryReader(fileStream);
|
||||
|
||||
byte[] data = new byte[fileStream.Length];
|
||||
fileReader.Read(data, 0, data.Length);
|
||||
|
||||
byte[] fileType = new byte[8];
|
||||
Array.Copy(data, 0, fileType, 0, fileType.Length);
|
||||
|
||||
FileType = ASCIIEncoding.ASCII.GetString(fileType);
|
||||
if (FileType != "HDMV0100" &&
|
||||
FileType != "HDMV0200")
|
||||
{
|
||||
throw new Exception(string.Format(
|
||||
"Clip info file {0} has an unknown file type {1}.",
|
||||
FileInfo.Name, FileType));
|
||||
}
|
||||
#if DEBUG
|
||||
Debug.WriteLine(string.Format(
|
||||
"\tFileType: {0}", FileType));
|
||||
#endif
|
||||
int clipIndex =
|
||||
((int)data[12] << 24) +
|
||||
((int)data[13] << 16) +
|
||||
((int)data[14] << 8) +
|
||||
((int)data[15]);
|
||||
|
||||
int clipLength =
|
||||
((int)data[clipIndex] << 24) +
|
||||
((int)data[clipIndex + 1] << 16) +
|
||||
((int)data[clipIndex + 2] << 8) +
|
||||
((int)data[clipIndex + 3]);
|
||||
|
||||
byte[] clipData = new byte[clipLength];
|
||||
Array.Copy(data, clipIndex + 4, clipData, 0, clipData.Length);
|
||||
|
||||
int streamCount = clipData[8];
|
||||
#if DEBUG
|
||||
Debug.WriteLine(string.Format(
|
||||
"\tStreamCount: {0}", streamCount));
|
||||
#endif
|
||||
int streamOffset = 10;
|
||||
for (int streamIndex = 0;
|
||||
streamIndex < streamCount;
|
||||
streamIndex++)
|
||||
{
|
||||
TSStream stream = null;
|
||||
|
||||
ushort PID = (ushort)
|
||||
((clipData[streamOffset] << 8) +
|
||||
clipData[streamOffset + 1]);
|
||||
|
||||
streamOffset += 2;
|
||||
|
||||
TSStreamType streamType = (TSStreamType)
|
||||
clipData[streamOffset + 1];
|
||||
switch (streamType)
|
||||
{
|
||||
case TSStreamType.MVC_VIDEO:
|
||||
// TODO
|
||||
break;
|
||||
|
||||
case TSStreamType.AVC_VIDEO:
|
||||
case TSStreamType.MPEG1_VIDEO:
|
||||
case TSStreamType.MPEG2_VIDEO:
|
||||
case TSStreamType.VC1_VIDEO:
|
||||
{
|
||||
TSVideoFormat videoFormat = (TSVideoFormat)
|
||||
(clipData[streamOffset + 2] >> 4);
|
||||
TSFrameRate frameRate = (TSFrameRate)
|
||||
(clipData[streamOffset + 2] & 0xF);
|
||||
TSAspectRatio aspectRatio = (TSAspectRatio)
|
||||
(clipData[streamOffset + 3] >> 4);
|
||||
|
||||
stream = new TSVideoStream();
|
||||
((TSVideoStream)stream).VideoFormat = videoFormat;
|
||||
((TSVideoStream)stream).AspectRatio = aspectRatio;
|
||||
((TSVideoStream)stream).FrameRate = frameRate;
|
||||
#if DEBUG
|
||||
Debug.WriteLine(string.Format(
|
||||
"\t{0} {1} {2} {3} {4}",
|
||||
PID,
|
||||
streamType,
|
||||
videoFormat,
|
||||
frameRate,
|
||||
aspectRatio));
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case TSStreamType.AC3_AUDIO:
|
||||
case TSStreamType.AC3_PLUS_AUDIO:
|
||||
case TSStreamType.AC3_PLUS_SECONDARY_AUDIO:
|
||||
case TSStreamType.AC3_TRUE_HD_AUDIO:
|
||||
case TSStreamType.DTS_AUDIO:
|
||||
case TSStreamType.DTS_HD_AUDIO:
|
||||
case TSStreamType.DTS_HD_MASTER_AUDIO:
|
||||
case TSStreamType.DTS_HD_SECONDARY_AUDIO:
|
||||
case TSStreamType.LPCM_AUDIO:
|
||||
case TSStreamType.MPEG1_AUDIO:
|
||||
case TSStreamType.MPEG2_AUDIO:
|
||||
{
|
||||
byte[] languageBytes = new byte[3];
|
||||
Array.Copy(clipData, streamOffset + 3,
|
||||
languageBytes, 0, languageBytes.Length);
|
||||
string languageCode =
|
||||
ASCIIEncoding.ASCII.GetString(languageBytes);
|
||||
|
||||
TSChannelLayout channelLayout = (TSChannelLayout)
|
||||
(clipData[streamOffset + 2] >> 4);
|
||||
TSSampleRate sampleRate = (TSSampleRate)
|
||||
(clipData[streamOffset + 2] & 0xF);
|
||||
|
||||
stream = new TSAudioStream();
|
||||
((TSAudioStream)stream).LanguageCode = languageCode;
|
||||
((TSAudioStream)stream).ChannelLayout = channelLayout;
|
||||
((TSAudioStream)stream).SampleRate = TSAudioStream.ConvertSampleRate(sampleRate);
|
||||
((TSAudioStream)stream).LanguageCode = languageCode;
|
||||
#if DEBUG
|
||||
Debug.WriteLine(string.Format(
|
||||
"\t{0} {1} {2} {3} {4}",
|
||||
PID,
|
||||
streamType,
|
||||
languageCode,
|
||||
channelLayout,
|
||||
sampleRate));
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case TSStreamType.INTERACTIVE_GRAPHICS:
|
||||
case TSStreamType.PRESENTATION_GRAPHICS:
|
||||
{
|
||||
byte[] languageBytes = new byte[3];
|
||||
Array.Copy(clipData, streamOffset + 2,
|
||||
languageBytes, 0, languageBytes.Length);
|
||||
string languageCode =
|
||||
ASCIIEncoding.ASCII.GetString(languageBytes);
|
||||
|
||||
stream = new TSGraphicsStream();
|
||||
stream.LanguageCode = languageCode;
|
||||
#if DEBUG
|
||||
Debug.WriteLine(string.Format(
|
||||
"\t{0} {1} {2}",
|
||||
PID,
|
||||
streamType,
|
||||
languageCode));
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case TSStreamType.SUBTITLE:
|
||||
{
|
||||
byte[] languageBytes = new byte[3];
|
||||
Array.Copy(clipData, streamOffset + 3,
|
||||
languageBytes, 0, languageBytes.Length);
|
||||
string languageCode =
|
||||
ASCIIEncoding.ASCII.GetString(languageBytes);
|
||||
#if DEBUG
|
||||
Debug.WriteLine(string.Format(
|
||||
"\t{0} {1} {2}",
|
||||
PID,
|
||||
streamType,
|
||||
languageCode));
|
||||
#endif
|
||||
stream = new TSTextStream();
|
||||
stream.LanguageCode = languageCode;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (stream != null)
|
||||
{
|
||||
stream.PID = PID;
|
||||
stream.StreamType = streamType;
|
||||
Streams.Add(PID, stream);
|
||||
}
|
||||
|
||||
streamOffset += clipData[streamOffset] + 1;
|
||||
}
|
||||
IsValid = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (fileReader != null) fileReader.Close();
|
||||
if (fileStream != null) fileStream.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1551
BDInfo/TSStreamFile.cs
Normal file
1551
BDInfo/TSStreamFile.cs
Normal file
File diff suppressed because it is too large
Load Diff
3
Bootstrapper/readme.txt
Normal file
3
Bootstrapper/readme.txt
Normal file
@ -0,0 +1,3 @@
|
||||
If Publishing, the folders in here must be copied to:
|
||||
|
||||
C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\Bootstrapper\Packages
|
20
Bootstrapper/vcredist10_x86/en/package.xml
Normal file
20
Bootstrapper/vcredist10_x86/en/package.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
|
||||
<Package
|
||||
xmlns="http://schemas.microsoft.com/developer/2004/01/bootstrapper"
|
||||
Name="DisplayName"
|
||||
Culture="Culture"
|
||||
>
|
||||
|
||||
<!-- Defines a localizable string table for error messages-->
|
||||
<Strings>
|
||||
<String Name="DisplayName">Visual C++ 2010 Runtime Libraries (x86)</String>
|
||||
<String Name="Culture">en</String>
|
||||
<String Name="AdminRequired">You do not have the permissions required to install Visual C++ 2010 Runtime Libraries (x86). Please contact your administrator.</String>
|
||||
<String Name="InvalidPlatformWin9x">Installation of Visual C++ 2010 Runtime Libraries (x86) is not supported on Windows 95. Contact your application vendor.</String>
|
||||
<String Name="InvalidPlatformWinNT">Installation of Visual C++ 2010 Runtime Libraries (x86) is not supported on Windows NT 4.0. Contact your application vendor.</String>
|
||||
<String Name="GeneralFailure">A failure occurred attempting to install Visual C++ 2010 Runtime Libraries (x86).</String>
|
||||
<String Name="VCRedistExe">http://go.microsoft.com/fwlink/?LinkID=210621</String>
|
||||
</Strings>
|
||||
|
||||
</Package>
|
45
Bootstrapper/vcredist10_x86/product.xml
Normal file
45
Bootstrapper/vcredist10_x86/product.xml
Normal file
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
|
||||
<Product
|
||||
xmlns="http://schemas.microsoft.com/developer/2004/01/bootstrapper"
|
||||
ProductCode="Microsoft.Visual.C++.10.0.x86"
|
||||
>
|
||||
|
||||
<!-- Defines list of files to be copied on build -->
|
||||
<PackageFiles>
|
||||
<PackageFile Name="vcredist_x86.exe" HomeSite="VCRedistExe"/>
|
||||
</PackageFiles>
|
||||
<InstallChecks>
|
||||
<MsiProductCheck Property="VCRedistInstalled" Product="{F0C3E5D1-1ADE-321E-8167-68EF0DE699A5}"/>
|
||||
</InstallChecks>
|
||||
|
||||
<!-- Defines how to invoke the setup for the Visual C++ 10.0 redist -->
|
||||
<!-- TODO: Needs EstrimatedTempSpace, LogFile, and an update of EstimatedDiskSpace -->
|
||||
<Commands Reboot="Defer">
|
||||
<Command PackageFile="vcredist_x86.exe"
|
||||
Arguments=' /q:a '
|
||||
>
|
||||
|
||||
<!-- These checks determine whether the package is to be installed -->
|
||||
<InstallConditions>
|
||||
<BypassIf Property="VCRedistInstalled" Compare="ValueGreaterThanOrEqualTo" Value="3"/>
|
||||
<!-- Block install if user does not have admin privileges -->
|
||||
<FailIf Property="AdminUser" Compare="ValueEqualTo" Value="false" String="AdminRequired"/>
|
||||
|
||||
<!-- Block install on Win95 -->
|
||||
<FailIf Property="Version9X" Compare="VersionLessThan" Value="4.10" String="InvalidPlatformWin9x"/>
|
||||
|
||||
<!-- Block install on NT 4 or less -->
|
||||
<FailIf Property="VersionNT" Compare="VersionLessThan" Value="5.00" String="InvalidPlatformWinNT"/>
|
||||
|
||||
</InstallConditions>
|
||||
|
||||
<ExitCodes>
|
||||
<ExitCode Value="0" Result="Success"/>
|
||||
<ExitCode Value="3010" Result="SuccessReboot"/>
|
||||
<DefaultExitCode Result="Fail" FormatMessageFromSystem="true" String="GeneralFailure" />
|
||||
</ExitCodes>
|
||||
|
||||
</Command>
|
||||
</Commands>
|
||||
</Product>
|
@ -1,438 +1,78 @@
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains some helpers for the api
|
||||
/// </summary>
|
||||
public static class ApiService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets an Item by Id, or the root item if none is supplied
|
||||
/// </summary>
|
||||
public static BaseItem GetItemById(string id)
|
||||
{
|
||||
Guid guid = string.IsNullOrEmpty(id) ? Guid.Empty : new Guid(id);
|
||||
|
||||
return Kernel.Instance.GetItemById(guid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a User by Id
|
||||
/// </summary>
|
||||
/// <param name="logActivity">Whether or not to update the user's LastActivityDate</param>
|
||||
public static User GetUserById(string id, bool logActivity)
|
||||
{
|
||||
var guid = new Guid(id);
|
||||
|
||||
var user = Kernel.Instance.Users.FirstOrDefault(u => u.Id == guid);
|
||||
|
||||
if (logActivity)
|
||||
{
|
||||
LogUserActivity(user);
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default User
|
||||
/// </summary>
|
||||
/// <param name="logActivity">Whether or not to update the user's LastActivityDate</param>
|
||||
public static User GetDefaultUser(bool logActivity)
|
||||
{
|
||||
User user = Kernel.Instance.GetDefaultUser();
|
||||
|
||||
if (logActivity)
|
||||
{
|
||||
LogUserActivity(user);
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates LastActivityDate for a given User
|
||||
/// </summary>
|
||||
public static void LogUserActivity(User user)
|
||||
{
|
||||
user.LastActivityDate = DateTime.UtcNow;
|
||||
Kernel.Instance.SaveUser(user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a BaseItem to a DTOBaseItem
|
||||
/// </summary>
|
||||
public async static Task<DtoBaseItem> GetDtoBaseItem(BaseItem item, User user,
|
||||
bool includeChildren = true,
|
||||
bool includePeople = true)
|
||||
{
|
||||
var dto = new DtoBaseItem();
|
||||
|
||||
var tasks = new List<Task>();
|
||||
|
||||
tasks.Add(AttachStudios(dto, item));
|
||||
|
||||
if (includeChildren)
|
||||
{
|
||||
tasks.Add(AttachChildren(dto, item, user));
|
||||
tasks.Add(AttachLocalTrailers(dto, item, user));
|
||||
}
|
||||
|
||||
if (includePeople)
|
||||
{
|
||||
tasks.Add(AttachPeople(dto, item));
|
||||
}
|
||||
|
||||
AttachBasicFields(dto, item, user);
|
||||
|
||||
// Make sure all the tasks we kicked off have completed.
|
||||
if (tasks.Count > 0)
|
||||
{
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets simple property values on a DTOBaseItem
|
||||
/// </summary>
|
||||
private static void AttachBasicFields(DtoBaseItem dto, BaseItem item, User user)
|
||||
{
|
||||
dto.AspectRatio = item.AspectRatio;
|
||||
dto.BackdropCount = item.BackdropImagePaths == null ? 0 : item.BackdropImagePaths.Count();
|
||||
dto.DateCreated = item.DateCreated;
|
||||
dto.DisplayMediaType = item.DisplayMediaType;
|
||||
|
||||
if (item.Genres != null)
|
||||
{
|
||||
dto.Genres = item.Genres.ToArray();
|
||||
}
|
||||
|
||||
dto.HasArt = !string.IsNullOrEmpty(item.ArtImagePath);
|
||||
dto.HasBanner = !string.IsNullOrEmpty(item.BannerImagePath);
|
||||
dto.HasLogo = !string.IsNullOrEmpty(item.LogoImagePath);
|
||||
dto.HasPrimaryImage = !string.IsNullOrEmpty(item.PrimaryImagePath);
|
||||
dto.HasThumb = !string.IsNullOrEmpty(item.ThumbnailImagePath);
|
||||
dto.Id = item.Id;
|
||||
dto.IsNew = item.IsRecentlyAdded(user);
|
||||
dto.IndexNumber = item.IndexNumber;
|
||||
dto.IsFolder = item.IsFolder;
|
||||
dto.Language = item.Language;
|
||||
dto.LocalTrailerCount = item.LocalTrailers == null ? 0 : item.LocalTrailers.Count();
|
||||
dto.Name = item.Name;
|
||||
dto.OfficialRating = item.OfficialRating;
|
||||
dto.Overview = item.Overview;
|
||||
|
||||
// If there are no backdrops, indicate what parent has them in case the Ui wants to allow inheritance
|
||||
if (dto.BackdropCount == 0)
|
||||
{
|
||||
int backdropCount;
|
||||
dto.ParentBackdropItemId = GetParentBackdropItemId(item, out backdropCount);
|
||||
dto.ParentBackdropCount = backdropCount;
|
||||
}
|
||||
|
||||
if (item.Parent != null)
|
||||
{
|
||||
dto.ParentId = item.Parent.Id;
|
||||
}
|
||||
|
||||
dto.ParentIndexNumber = item.ParentIndexNumber;
|
||||
|
||||
// If there is no logo, indicate what parent has one in case the Ui wants to allow inheritance
|
||||
if (!dto.HasLogo)
|
||||
{
|
||||
dto.ParentLogoItemId = GetParentLogoItemId(item);
|
||||
}
|
||||
|
||||
dto.Path = item.Path;
|
||||
|
||||
dto.PremiereDate = item.PremiereDate;
|
||||
dto.ProductionYear = item.ProductionYear;
|
||||
dto.ProviderIds = item.ProviderIds;
|
||||
dto.RunTimeTicks = item.RunTimeTicks;
|
||||
dto.SortName = item.SortName;
|
||||
|
||||
if (item.Taglines != null)
|
||||
{
|
||||
dto.Taglines = item.Taglines.ToArray();
|
||||
}
|
||||
|
||||
dto.TrailerUrl = item.TrailerUrl;
|
||||
dto.Type = item.GetType().Name;
|
||||
dto.CommunityRating = item.CommunityRating;
|
||||
|
||||
dto.UserData = GetDtoUserItemData(item.GetUserData(user, false));
|
||||
|
||||
var folder = item as Folder;
|
||||
|
||||
if (folder != null)
|
||||
{
|
||||
dto.SpecialCounts = folder.GetSpecialCounts(user);
|
||||
|
||||
dto.IsRoot = folder.IsRoot;
|
||||
dto.IsVirtualFolder = folder.IsVirtualFolder;
|
||||
}
|
||||
|
||||
// Add AudioInfo
|
||||
var audio = item as Audio;
|
||||
|
||||
if (audio != null)
|
||||
{
|
||||
dto.AudioInfo = new AudioInfo
|
||||
{
|
||||
Album = audio.Album,
|
||||
AlbumArtist = audio.AlbumArtist,
|
||||
Artist = audio.Artist,
|
||||
BitRate = audio.BitRate,
|
||||
Channels = audio.Channels
|
||||
};
|
||||
}
|
||||
|
||||
// Add VideoInfo
|
||||
var video = item as Video;
|
||||
|
||||
if (video != null)
|
||||
{
|
||||
dto.VideoInfo = new VideoInfo
|
||||
{
|
||||
Height = video.Height,
|
||||
Width = video.Width,
|
||||
Codec = video.Codec,
|
||||
VideoType = video.VideoType,
|
||||
ScanType = video.ScanType
|
||||
};
|
||||
|
||||
if (video.AudioStreams != null)
|
||||
{
|
||||
dto.VideoInfo.AudioStreams = video.AudioStreams.ToArray();
|
||||
}
|
||||
|
||||
if (video.Subtitles != null)
|
||||
{
|
||||
dto.VideoInfo.Subtitles = video.Subtitles.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
// Add SeriesInfo
|
||||
var series = item as Series;
|
||||
|
||||
if (series != null)
|
||||
{
|
||||
DayOfWeek[] airDays = series.AirDays == null ? new DayOfWeek[] { } : series.AirDays.ToArray();
|
||||
|
||||
dto.SeriesInfo = new SeriesInfo
|
||||
{
|
||||
AirDays = airDays,
|
||||
AirTime = series.AirTime,
|
||||
Status = series.Status
|
||||
};
|
||||
}
|
||||
|
||||
// Add MovieInfo
|
||||
var movie = item as Movie;
|
||||
|
||||
if (movie != null)
|
||||
{
|
||||
int specialFeatureCount = movie.SpecialFeatures == null ? 0 : movie.SpecialFeatures.Count();
|
||||
|
||||
dto.MovieInfo = new MovieInfo
|
||||
{
|
||||
SpecialFeatureCount = specialFeatureCount
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches Studio DTO's to a DTOBaseItem
|
||||
/// </summary>
|
||||
private static async Task AttachStudios(DtoBaseItem dto, BaseItem item)
|
||||
{
|
||||
// Attach Studios by transforming them into BaseItemStudio (DTO)
|
||||
if (item.Studios != null)
|
||||
{
|
||||
Studio[] entities = await Task.WhenAll(item.Studios.Select(c => Kernel.Instance.ItemController.GetStudio(c))).ConfigureAwait(false);
|
||||
|
||||
dto.Studios = new BaseItemStudio[entities.Length];
|
||||
|
||||
for (int i = 0; i < entities.Length; i++)
|
||||
{
|
||||
Studio entity = entities[i];
|
||||
var baseItemStudio = new BaseItemStudio{};
|
||||
|
||||
baseItemStudio.Name = entity.Name;
|
||||
|
||||
baseItemStudio.HasImage = !string.IsNullOrEmpty(entity.PrimaryImagePath);
|
||||
|
||||
dto.Studios[i] = baseItemStudio;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches child DTO's to a DTOBaseItem
|
||||
/// </summary>
|
||||
private static async Task AttachChildren(DtoBaseItem dto, BaseItem item, User user)
|
||||
{
|
||||
var folder = item as Folder;
|
||||
|
||||
if (folder != null)
|
||||
{
|
||||
IEnumerable<BaseItem> children = folder.GetChildren(user);
|
||||
|
||||
dto.Children = await Task.WhenAll(children.Select(c => GetDtoBaseItem(c, user, false, false))).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches trailer DTO's to a DTOBaseItem
|
||||
/// </summary>
|
||||
private static async Task AttachLocalTrailers(DtoBaseItem dto, BaseItem item, User user)
|
||||
{
|
||||
if (item.LocalTrailers != null && item.LocalTrailers.Any())
|
||||
{
|
||||
dto.LocalTrailers = await Task.WhenAll(item.LocalTrailers.Select(c => GetDtoBaseItem(c, user, false, false))).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches People DTO's to a DTOBaseItem
|
||||
/// </summary>
|
||||
private static async Task AttachPeople(DtoBaseItem dto, BaseItem item)
|
||||
{
|
||||
// Attach People by transforming them into BaseItemPerson (DTO)
|
||||
if (item.People != null)
|
||||
{
|
||||
IEnumerable<Person> entities = await Task.WhenAll(item.People.Select(c => Kernel.Instance.ItemController.GetPerson(c.Key))).ConfigureAwait(false);
|
||||
|
||||
dto.People = item.People.Select(p =>
|
||||
{
|
||||
var baseItemPerson = new BaseItemPerson{};
|
||||
|
||||
baseItemPerson.Name = p.Key;
|
||||
baseItemPerson.Overview = p.Value.Overview;
|
||||
baseItemPerson.Type = p.Value.Type;
|
||||
|
||||
Person ibnObject = entities.First(i => i.Name.Equals(p.Key, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (ibnObject != null)
|
||||
{
|
||||
baseItemPerson.HasImage = !string.IsNullOrEmpty(ibnObject.PrimaryImagePath);
|
||||
}
|
||||
|
||||
return baseItemPerson;
|
||||
}).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If an item does not any backdrops, this can be used to find the first parent that does have one
|
||||
/// </summary>
|
||||
private static Guid? GetParentBackdropItemId(BaseItem item, out int backdropCount)
|
||||
{
|
||||
backdropCount = 0;
|
||||
|
||||
var parent = item.Parent;
|
||||
|
||||
while (parent != null)
|
||||
{
|
||||
if (parent.BackdropImagePaths != null && parent.BackdropImagePaths.Any())
|
||||
{
|
||||
backdropCount = parent.BackdropImagePaths.Count();
|
||||
return parent.Id;
|
||||
}
|
||||
|
||||
parent = parent.Parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If an item does not have a logo, this can be used to find the first parent that does have one
|
||||
/// </summary>
|
||||
private static Guid? GetParentLogoItemId(BaseItem item)
|
||||
{
|
||||
var parent = item.Parent;
|
||||
|
||||
while (parent != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(parent.LogoImagePath))
|
||||
{
|
||||
return parent.Id;
|
||||
}
|
||||
|
||||
parent = parent.Parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an ImagesByName entity along with the number of items containing it
|
||||
/// </summary>
|
||||
public static IbnItem GetIbnItem(BaseEntity entity, int itemCount)
|
||||
{
|
||||
return new IbnItem
|
||||
{
|
||||
Id = entity.Id,
|
||||
BaseItemCount = itemCount,
|
||||
HasImage = !string.IsNullOrEmpty(entity.PrimaryImagePath),
|
||||
Name = entity.Name
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a User to a DTOUser
|
||||
/// </summary>
|
||||
public static DtoUser GetDtoUser(User user)
|
||||
{
|
||||
return new DtoUser
|
||||
{
|
||||
Id = user.Id,
|
||||
Name = user.Name,
|
||||
HasImage = !string.IsNullOrEmpty(user.PrimaryImagePath),
|
||||
HasPassword = !string.IsNullOrEmpty(user.Password),
|
||||
LastActivityDate = user.LastActivityDate,
|
||||
LastLoginDate = user.LastLoginDate
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a UserItemData to a DTOUserItemData
|
||||
/// </summary>
|
||||
public static DtoUserItemData GetDtoUserItemData(UserItemData data)
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DtoUserItemData
|
||||
{
|
||||
IsFavorite = data.IsFavorite,
|
||||
Likes = data.Likes,
|
||||
PlaybackPositionTicks = data.PlaybackPositionTicks,
|
||||
PlayCount = data.PlayCount,
|
||||
Rating = data.Rating
|
||||
};
|
||||
}
|
||||
|
||||
public static bool IsApiUrlMatch(string url, HttpListenerRequest request)
|
||||
{
|
||||
url = "/api/" + url;
|
||||
|
||||
return request.Url.LocalPath.EndsWith(url, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Connectivity;
|
||||
using ServiceStack.Common.Web;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains some helpers for the api
|
||||
/// </summary>
|
||||
public static class ApiService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a User by Id
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the user</param>
|
||||
/// <returns>User.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">id</exception>
|
||||
public static User GetUserById(string id)
|
||||
{
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
throw new ArgumentNullException("id");
|
||||
}
|
||||
|
||||
var guid = new Guid(id);
|
||||
|
||||
return Kernel.Instance.GetUserById(guid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether [is API URL match] [the specified URL].
|
||||
/// </summary>
|
||||
/// <param name="url">The URL.</param>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns><c>true</c> if [is API URL match] [the specified URL]; otherwise, <c>false</c>.</returns>
|
||||
public static bool IsApiUrlMatch(string url, HttpListenerRequest request)
|
||||
{
|
||||
url = "/api/" + url;
|
||||
|
||||
return request.Url.LocalPath.EndsWith(url, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// Gets the current user.
|
||||
///// </summary>
|
||||
///// <param name="request">The request.</param>
|
||||
///// <returns>Task{User}.</returns>
|
||||
//public static async Task<User> GetCurrentUser(AuthenticatedRequest request)
|
||||
//{
|
||||
// var user = GetUserById(request.UserId);
|
||||
|
||||
// if (user == null)
|
||||
// {
|
||||
// throw HttpError.Unauthorized("Invalid user or password entered.");
|
||||
// }
|
||||
|
||||
// var clientType = ClientType.Other;
|
||||
|
||||
// if (!string.IsNullOrEmpty(request.Client))
|
||||
// {
|
||||
// ClientType type;
|
||||
|
||||
// if (Enum.TryParse(request.Client, true, out type))
|
||||
// {
|
||||
// clientType = type;
|
||||
// }
|
||||
// }
|
||||
|
||||
// await Kernel.Instance.UserManager.LogUserActivity(user, clientType, request.Device).ConfigureAwait(false);
|
||||
|
||||
// return user;
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
@ -1,81 +0,0 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace MediaBrowser.Api.Drawing
|
||||
{
|
||||
public static class DrawingUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Resizes a set of dimensions
|
||||
/// </summary>
|
||||
public static Size Resize(int currentWidth, int currentHeight, int? width, int? height, int? maxWidth, int? maxHeight)
|
||||
{
|
||||
return Resize(new Size(currentWidth, currentHeight), width, height, maxWidth, maxHeight);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resizes a set of dimensions
|
||||
/// </summary>
|
||||
/// <param name="size">The original size object</param>
|
||||
/// <param name="width">A new fixed width, if desired</param>
|
||||
/// <param name="height">A new fixed neight, if desired</param>
|
||||
/// <param name="maxWidth">A max fixed width, if desired</param>
|
||||
/// <param name="maxHeight">A max fixed height, if desired</param>
|
||||
/// <returns>A new size object</returns>
|
||||
public static Size Resize(Size size, int? width, int? height, int? maxWidth, int? maxHeight)
|
||||
{
|
||||
decimal newWidth = size.Width;
|
||||
decimal newHeight = size.Height;
|
||||
|
||||
if (width.HasValue && height.HasValue)
|
||||
{
|
||||
newWidth = width.Value;
|
||||
newHeight = height.Value;
|
||||
}
|
||||
|
||||
else if (height.HasValue)
|
||||
{
|
||||
newWidth = GetNewWidth(newHeight, newWidth, height.Value);
|
||||
newHeight = height.Value;
|
||||
}
|
||||
|
||||
else if (width.HasValue)
|
||||
{
|
||||
newHeight = GetNewHeight(newHeight, newWidth, width.Value);
|
||||
newWidth = width.Value;
|
||||
}
|
||||
|
||||
if (maxHeight.HasValue && maxHeight < newHeight)
|
||||
{
|
||||
newWidth = GetNewWidth(newHeight, newWidth, maxHeight.Value);
|
||||
newHeight = maxHeight.Value;
|
||||
}
|
||||
|
||||
if (maxWidth.HasValue && maxWidth < newWidth)
|
||||
{
|
||||
newHeight = GetNewHeight(newHeight, newWidth, maxWidth.Value);
|
||||
newWidth = maxWidth.Value;
|
||||
}
|
||||
|
||||
return new Size(Convert.ToInt32(newWidth), Convert.ToInt32(newHeight));
|
||||
}
|
||||
|
||||
private static decimal GetNewWidth(decimal currentHeight, decimal currentWidth, int newHeight)
|
||||
{
|
||||
decimal scaleFactor = newHeight;
|
||||
scaleFactor /= currentHeight;
|
||||
scaleFactor *= currentWidth;
|
||||
|
||||
return scaleFactor;
|
||||
}
|
||||
|
||||
private static decimal GetNewHeight(decimal currentHeight, decimal currentWidth, int newWidth)
|
||||
{
|
||||
decimal scaleFactor = newWidth;
|
||||
scaleFactor /= currentWidth;
|
||||
scaleFactor *= currentHeight;
|
||||
|
||||
return scaleFactor;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,148 +0,0 @@
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Api.Drawing
|
||||
{
|
||||
public static class ImageProcessor
|
||||
{
|
||||
/// <summary>
|
||||
/// Processes an image by resizing to target dimensions
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity that owns the image</param>
|
||||
/// <param name="imageType">The image type</param>
|
||||
/// <param name="imageIndex">The image index (currently only used with backdrops)</param>
|
||||
/// <param name="toStream">The stream to save the new image to</param>
|
||||
/// <param name="width">Use if a fixed width is required. Aspect ratio will be preserved.</param>
|
||||
/// <param name="height">Use if a fixed height is required. Aspect ratio will be preserved.</param>
|
||||
/// <param name="maxWidth">Use if a max width is required. Aspect ratio will be preserved.</param>
|
||||
/// <param name="maxHeight">Use if a max height is required. Aspect ratio will be preserved.</param>
|
||||
/// <param name="quality">Quality level, from 0-100. Currently only applies to JPG. The default value should suffice.</param>
|
||||
public static void ProcessImage(BaseEntity entity, ImageType imageType, int imageIndex, Stream toStream, int? width, int? height, int? maxWidth, int? maxHeight, int? quality)
|
||||
{
|
||||
Image originalImage = Image.FromFile(GetImagePath(entity, imageType, imageIndex));
|
||||
|
||||
// Determine the output size based on incoming parameters
|
||||
Size newSize = DrawingUtils.Resize(originalImage.Size, width, height, maxWidth, maxHeight);
|
||||
|
||||
Bitmap thumbnail;
|
||||
|
||||
// Graphics.FromImage will throw an exception if the PixelFormat is Indexed, so we need to handle that here
|
||||
if (originalImage.PixelFormat.HasFlag(PixelFormat.Indexed))
|
||||
{
|
||||
thumbnail = new Bitmap(originalImage, newSize.Width, newSize.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
thumbnail = new Bitmap(newSize.Width, newSize.Height, originalImage.PixelFormat);
|
||||
}
|
||||
|
||||
thumbnail.MakeTransparent();
|
||||
|
||||
// Preserve the original resolution
|
||||
thumbnail.SetResolution(originalImage.HorizontalResolution, originalImage.VerticalResolution);
|
||||
|
||||
Graphics thumbnailGraph = Graphics.FromImage(thumbnail);
|
||||
|
||||
thumbnailGraph.CompositingQuality = CompositingQuality.HighQuality;
|
||||
thumbnailGraph.SmoothingMode = SmoothingMode.HighQuality;
|
||||
thumbnailGraph.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
thumbnailGraph.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
thumbnailGraph.CompositingMode = CompositingMode.SourceOver;
|
||||
|
||||
thumbnailGraph.DrawImage(originalImage, 0, 0, newSize.Width, newSize.Height);
|
||||
|
||||
ImageFormat outputFormat = originalImage.RawFormat;
|
||||
|
||||
// Write to the output stream
|
||||
SaveImage(outputFormat, thumbnail, toStream, quality);
|
||||
|
||||
thumbnailGraph.Dispose();
|
||||
thumbnail.Dispose();
|
||||
originalImage.Dispose();
|
||||
}
|
||||
|
||||
public static string GetImagePath(BaseEntity entity, ImageType imageType, int imageIndex)
|
||||
{
|
||||
var item = entity as BaseItem;
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
if (imageType == ImageType.Logo)
|
||||
{
|
||||
return item.LogoImagePath;
|
||||
}
|
||||
if (imageType == ImageType.Backdrop)
|
||||
{
|
||||
return item.BackdropImagePaths.ElementAt(imageIndex);
|
||||
}
|
||||
if (imageType == ImageType.Banner)
|
||||
{
|
||||
return item.BannerImagePath;
|
||||
}
|
||||
if (imageType == ImageType.Art)
|
||||
{
|
||||
return item.ArtImagePath;
|
||||
}
|
||||
if (imageType == ImageType.Thumbnail)
|
||||
{
|
||||
return item.ThumbnailImagePath;
|
||||
}
|
||||
}
|
||||
|
||||
return entity.PrimaryImagePath;
|
||||
}
|
||||
|
||||
public static void SaveImage(ImageFormat outputFormat, Image newImage, Stream toStream, int? quality)
|
||||
{
|
||||
// Use special save methods for jpeg and png that will result in a much higher quality image
|
||||
// All other formats use the generic Image.Save
|
||||
if (ImageFormat.Jpeg.Equals(outputFormat))
|
||||
{
|
||||
SaveJpeg(newImage, toStream, quality);
|
||||
}
|
||||
else if (ImageFormat.Png.Equals(outputFormat))
|
||||
{
|
||||
newImage.Save(toStream, ImageFormat.Png);
|
||||
}
|
||||
else
|
||||
{
|
||||
newImage.Save(toStream, outputFormat);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SaveJpeg(Image image, Stream target, int? quality)
|
||||
{
|
||||
if (!quality.HasValue)
|
||||
{
|
||||
quality = 90;
|
||||
}
|
||||
|
||||
using (var encoderParameters = new EncoderParameters(1))
|
||||
{
|
||||
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, quality.Value);
|
||||
image.Save(target, GetImageCodecInfo("image/jpeg"), encoderParameters);
|
||||
}
|
||||
}
|
||||
|
||||
public static ImageCodecInfo GetImageCodecInfo(string mimeType)
|
||||
{
|
||||
ImageCodecInfo[] info = ImageCodecInfo.GetImageEncoders();
|
||||
|
||||
for (int i = 0; i < info.Length; i++)
|
||||
{
|
||||
ImageCodecInfo ici = info[i];
|
||||
if (ici.MimeType.Equals(mimeType, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return ici;
|
||||
}
|
||||
}
|
||||
return info[1];
|
||||
}
|
||||
}
|
||||
}
|
200
MediaBrowser.Api/EnvironmentService.cs
Normal file
200
MediaBrowser.Api/EnvironmentService.cs
Normal file
@ -0,0 +1,200 @@
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Model.IO;
|
||||
using ServiceStack.ServiceHost;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetDirectoryContents
|
||||
/// </summary>
|
||||
[Route("/Environment/DirectoryContents", "GET")]
|
||||
public class GetDirectoryContents : IReturn<List<FileSystemEntryInfo>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the path.
|
||||
/// </summary>
|
||||
/// <value>The path.</value>
|
||||
public string Path { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether [include files].
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if [include files]; otherwise, <c>false</c>.</value>
|
||||
public bool IncludeFiles { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether [include directories].
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if [include directories]; otherwise, <c>false</c>.</value>
|
||||
public bool IncludeDirectories { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether [include hidden].
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if [include hidden]; otherwise, <c>false</c>.</value>
|
||||
public bool IncludeHidden { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetDrives
|
||||
/// </summary>
|
||||
[Route("/Environment/Drives", "GET")]
|
||||
public class GetDrives : IReturn<List<FileSystemEntryInfo>>
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetNetworkComputers
|
||||
/// </summary>
|
||||
[Route("/Environment/NetworkComputers", "GET")]
|
||||
public class GetNetworkComputers : IReturn<List<FileSystemEntryInfo>>
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class EnvironmentService
|
||||
/// </summary>
|
||||
[Export(typeof(IRestfulService))]
|
||||
public class EnvironmentService : BaseRestService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">Path</exception>
|
||||
/// <exception cref="System.ArgumentException"></exception>
|
||||
public object Get(GetDirectoryContents request)
|
||||
{
|
||||
var path = request.Path;
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new ArgumentNullException("Path");
|
||||
}
|
||||
|
||||
// Reject invalid input
|
||||
if (!Path.IsPathRooted(path))
|
||||
{
|
||||
throw new ArgumentException(string.Format("Invalid path: {0}", path));
|
||||
}
|
||||
|
||||
if (path.StartsWith(NetworkPrefix, StringComparison.OrdinalIgnoreCase) && path.LastIndexOf('\\') == 1)
|
||||
{
|
||||
return GetNetworkShares(path).ToList();
|
||||
}
|
||||
|
||||
return GetFileSystemEntries(request).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetDrives request)
|
||||
{
|
||||
return GetDrives().ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetNetworkComputers request)
|
||||
{
|
||||
return GetNetworkComputers().ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list that is returned when an empty path is supplied
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable{FileSystemEntryInfo}.</returns>
|
||||
private IEnumerable<FileSystemEntryInfo> GetDrives()
|
||||
{
|
||||
// Only include drives in the ready state or this method could end up being very slow, waiting for drives to timeout
|
||||
return DriveInfo.GetDrives().Where(d => d.IsReady).Select(d => new FileSystemEntryInfo
|
||||
{
|
||||
Name = GetName(d),
|
||||
Path = d.RootDirectory.FullName,
|
||||
Type = FileSystemEntryType.Directory
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the network computers.
|
||||
/// </summary>
|
||||
/// <returns>IEnumerable{FileSystemEntryInfo}.</returns>
|
||||
private IEnumerable<FileSystemEntryInfo> GetNetworkComputers()
|
||||
{
|
||||
return NetUtils.GetNetworkComputers().Select(c => new FileSystemEntryInfo
|
||||
{
|
||||
Name = c,
|
||||
Path = NetworkPrefix + c,
|
||||
Type = FileSystemEntryType.NetworkComputer
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
/// <param name="drive">The drive.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
private string GetName(DriveInfo drive)
|
||||
{
|
||||
return drive.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the network shares.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns>IEnumerable{FileSystemEntryInfo}.</returns>
|
||||
private IEnumerable<FileSystemEntryInfo> GetNetworkShares(string path)
|
||||
{
|
||||
return new ShareCollection(path).OfType<Share>().Where(s => s.ShareType == ShareType.Disk).Select(c => new FileSystemEntryInfo
|
||||
{
|
||||
Name = c.NetName,
|
||||
Path = Path.Combine(path, c.NetName),
|
||||
Type = FileSystemEntryType.NetworkShare
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file system entries.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>IEnumerable{FileSystemEntryInfo}.</returns>
|
||||
private IEnumerable<FileSystemEntryInfo> GetFileSystemEntries(GetDirectoryContents request)
|
||||
{
|
||||
var fileSystemEntries = FileSystem.GetFileSystemEntries(request.Path, "*", request.IncludeFiles, request.IncludeDirectories).Where(f => !f.IsSystemFile);
|
||||
|
||||
if (!request.IncludeHidden)
|
||||
{
|
||||
fileSystemEntries = fileSystemEntries.Where(f => !f.IsHidden);
|
||||
}
|
||||
|
||||
return fileSystemEntries.Select(f => new FileSystemEntryInfo
|
||||
{
|
||||
Name = f.cFileName,
|
||||
Path = f.Path,
|
||||
Type = f.IsDirectory ? FileSystemEntryType.Directory : FileSystemEntryType.File
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the network prefix.
|
||||
/// </summary>
|
||||
/// <value>The network prefix.</value>
|
||||
private string NetworkPrefix
|
||||
{
|
||||
get { return Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture) + Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture); }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Supported output formats are: mp3,flac,ogg,wav,asf,wma,aac
|
||||
/// </summary>
|
||||
[Export(typeof(BaseHandler))]
|
||||
public class AudioHandler : BaseMediaHandler<Audio, AudioOutputFormats>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("audio", request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We can output these formats directly, but we cannot encode to them.
|
||||
/// </summary>
|
||||
protected override IEnumerable<AudioOutputFormats> UnsupportedOutputEncodingFormats
|
||||
{
|
||||
get
|
||||
{
|
||||
return new AudioOutputFormats[] { AudioOutputFormats.Aac, AudioOutputFormats.Flac, AudioOutputFormats.Wma };
|
||||
}
|
||||
}
|
||||
|
||||
private int? GetMaxAcceptedBitRate(AudioOutputFormats audioFormat)
|
||||
{
|
||||
return GetMaxAcceptedBitRate(audioFormat.ToString());
|
||||
}
|
||||
|
||||
private int? GetMaxAcceptedBitRate(string audioFormat)
|
||||
{
|
||||
if (audioFormat.Equals("mp3", System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return 320000;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether or not the original file requires transcoding
|
||||
/// </summary>
|
||||
protected override bool RequiresConversion()
|
||||
{
|
||||
if (base.RequiresConversion())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
string currentFormat = Path.GetExtension(LibraryItem.Path).Replace(".", string.Empty);
|
||||
|
||||
int? bitrate = GetMaxAcceptedBitRate(currentFormat);
|
||||
|
||||
// If the bitrate is greater than our desired bitrate, we need to transcode
|
||||
if (bitrate.HasValue && bitrate.Value < LibraryItem.BitRate)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the number of channels is greater than our desired channels, we need to transcode
|
||||
if (AudioChannels.HasValue && AudioChannels.Value < LibraryItem.Channels)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the sample rate is greater than our desired sample rate, we need to transcode
|
||||
if (AudioSampleRate.HasValue && AudioSampleRate.Value < LibraryItem.SampleRate)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Yay
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates arguments to pass to ffmpeg
|
||||
/// </summary>
|
||||
protected override string GetCommandLineArguments()
|
||||
{
|
||||
var audioTranscodeParams = new List<string>();
|
||||
|
||||
AudioOutputFormats outputFormat = GetConversionOutputFormat();
|
||||
|
||||
int? bitrate = GetMaxAcceptedBitRate(outputFormat);
|
||||
|
||||
if (bitrate.HasValue)
|
||||
{
|
||||
audioTranscodeParams.Add("-ab " + bitrate.Value);
|
||||
}
|
||||
|
||||
int? channels = GetNumAudioChannelsParam(LibraryItem.Channels);
|
||||
|
||||
if (channels.HasValue)
|
||||
{
|
||||
audioTranscodeParams.Add("-ac " + channels.Value);
|
||||
}
|
||||
|
||||
int? sampleRate = GetSampleRateParam(LibraryItem.SampleRate);
|
||||
|
||||
if (sampleRate.HasValue)
|
||||
{
|
||||
audioTranscodeParams.Add("-ar " + sampleRate.Value);
|
||||
}
|
||||
|
||||
audioTranscodeParams.Add("-f " + outputFormat);
|
||||
|
||||
return "-i \"" + LibraryItem.Path + "\" -vn " + string.Join(" ", audioTranscodeParams.ToArray()) + " -";
|
||||
}
|
||||
}
|
||||
}
|
@ -1,255 +0,0 @@
|
||||
using MediaBrowser.Common.Logging;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
public abstract class BaseMediaHandler<TBaseItemType, TOutputType> : BaseHandler
|
||||
where TBaseItemType : BaseItem, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Supported values: mp3,flac,ogg,wav,asf,wma,aac
|
||||
/// </summary>
|
||||
protected virtual IEnumerable<TOutputType> OutputFormats
|
||||
{
|
||||
get
|
||||
{
|
||||
return QueryString["outputformats"].Split(',').Select(o => (TOutputType)Enum.Parse(typeof(TOutputType), o, true));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// These formats can be outputted directly but cannot be encoded to
|
||||
/// </summary>
|
||||
protected virtual IEnumerable<TOutputType> UnsupportedOutputEncodingFormats
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TOutputType[] { };
|
||||
}
|
||||
}
|
||||
|
||||
private TBaseItemType _libraryItem;
|
||||
/// <summary>
|
||||
/// Gets the library item that will be played, if any
|
||||
/// </summary>
|
||||
protected TBaseItemType LibraryItem
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_libraryItem == null)
|
||||
{
|
||||
string id = QueryString["id"];
|
||||
|
||||
if (!string.IsNullOrEmpty(id))
|
||||
{
|
||||
_libraryItem = Kernel.Instance.GetItemById(Guid.Parse(id)) as TBaseItemType;
|
||||
}
|
||||
}
|
||||
|
||||
return _libraryItem;
|
||||
}
|
||||
}
|
||||
|
||||
public int? AudioChannels
|
||||
{
|
||||
get
|
||||
{
|
||||
string val = QueryString["audiochannels"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
public int? AudioSampleRate
|
||||
{
|
||||
get
|
||||
{
|
||||
string val = QueryString["audiosamplerate"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return 44100;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
protected override Task<ResponseInfo> GetResponseInfo()
|
||||
{
|
||||
ResponseInfo info = new ResponseInfo
|
||||
{
|
||||
ContentType = MimeTypes.GetMimeType("." + GetConversionOutputFormat()),
|
||||
CompressResponse = false
|
||||
};
|
||||
|
||||
return Task.FromResult<ResponseInfo>(info);
|
||||
}
|
||||
|
||||
public override Task ProcessRequest(HttpListenerContext ctx)
|
||||
{
|
||||
HttpListenerContext = ctx;
|
||||
|
||||
if (!RequiresConversion())
|
||||
{
|
||||
return new StaticFileHandler { Path = LibraryItem.Path }.ProcessRequest(ctx);
|
||||
}
|
||||
|
||||
return base.ProcessRequest(ctx);
|
||||
}
|
||||
|
||||
protected abstract string GetCommandLineArguments();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the format we'll be converting to
|
||||
/// </summary>
|
||||
protected virtual TOutputType GetConversionOutputFormat()
|
||||
{
|
||||
return OutputFormats.First(f => !UnsupportedOutputEncodingFormats.Any(s => s.ToString().Equals(f.ToString(), StringComparison.OrdinalIgnoreCase)));
|
||||
}
|
||||
|
||||
protected virtual bool RequiresConversion()
|
||||
{
|
||||
string currentFormat = Path.GetExtension(LibraryItem.Path).Replace(".", string.Empty);
|
||||
|
||||
if (OutputFormats.Any(f => currentFormat.EndsWith(f.ToString(), StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
// We can output these files directly, but we can't encode them
|
||||
if (UnsupportedOutputEncodingFormats.Any(f => currentFormat.EndsWith(f.ToString(), StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If it's not in a format the consumer accepts, return true
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private FileStream LogFileStream { get; set; }
|
||||
|
||||
protected async override Task WriteResponseToOutputStream(Stream stream)
|
||||
{
|
||||
var startInfo = new ProcessStartInfo{};
|
||||
|
||||
startInfo.CreateNoWindow = true;
|
||||
|
||||
startInfo.UseShellExecute = false;
|
||||
|
||||
// Must consume both or ffmpeg may hang due to deadlocks. See comments below.
|
||||
startInfo.RedirectStandardOutput = true;
|
||||
startInfo.RedirectStandardError = true;
|
||||
|
||||
startInfo.FileName = Kernel.Instance.ApplicationPaths.FFMpegPath;
|
||||
startInfo.WorkingDirectory = Kernel.Instance.ApplicationPaths.FFMpegDirectory;
|
||||
startInfo.Arguments = GetCommandLineArguments();
|
||||
|
||||
Logger.LogInfo(startInfo.FileName + " " + startInfo.Arguments);
|
||||
|
||||
var process = new Process{};
|
||||
process.StartInfo = startInfo;
|
||||
|
||||
// FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
|
||||
LogFileStream = new FileStream(Path.Combine(Kernel.Instance.ApplicationPaths.LogDirectoryPath, "ffmpeg-" + Guid.NewGuid().ToString() + ".txt"), FileMode.Create);
|
||||
|
||||
process.EnableRaisingEvents = true;
|
||||
|
||||
process.Exited += ProcessExited;
|
||||
|
||||
try
|
||||
{
|
||||
process.Start();
|
||||
|
||||
// MUST read both stdout and stderr asynchronously or a deadlock may occurr
|
||||
|
||||
// Kick off two tasks
|
||||
Task mediaTask = process.StandardOutput.BaseStream.CopyToAsync(stream);
|
||||
Task debugLogTask = process.StandardError.BaseStream.CopyToAsync(LogFileStream);
|
||||
|
||||
await mediaTask.ConfigureAwait(false);
|
||||
//await debugLogTask.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogException(ex);
|
||||
|
||||
// Hate having to do this
|
||||
try
|
||||
{
|
||||
process.Kill();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessExited(object sender, EventArgs e)
|
||||
{
|
||||
if (LogFileStream != null)
|
||||
{
|
||||
LogFileStream.Dispose();
|
||||
}
|
||||
|
||||
var process = sender as Process;
|
||||
|
||||
Logger.LogInfo("FFMpeg exited with code " + process.ExitCode);
|
||||
|
||||
process.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of audio channels to specify on the command line
|
||||
/// </summary>
|
||||
protected int? GetNumAudioChannelsParam(int libraryItemChannels)
|
||||
{
|
||||
// If the user requested a max number of channels
|
||||
if (AudioChannels.HasValue)
|
||||
{
|
||||
// Only specify the param if we're going to downmix
|
||||
if (AudioChannels.Value < libraryItemChannels)
|
||||
{
|
||||
return AudioChannels.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of audio channels to specify on the command line
|
||||
/// </summary>
|
||||
protected int? GetSampleRateParam(int libraryItemSampleRate)
|
||||
{
|
||||
// If the user requested a max value
|
||||
if (AudioSampleRate.HasValue)
|
||||
{
|
||||
// Only specify the param if we're going to downmix
|
||||
if (AudioSampleRate.Value < libraryItemSampleRate)
|
||||
{
|
||||
return AudioSampleRate.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a handler to set user favorite status for an item
|
||||
/// </summary>
|
||||
[Export(typeof(BaseHandler))]
|
||||
public class FavoriteStatusHandler : BaseSerializationHandler<DtoUserItemData>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("FavoriteStatus", request);
|
||||
}
|
||||
|
||||
protected override Task<DtoUserItemData> GetObjectToSerialize()
|
||||
{
|
||||
// Get the item
|
||||
BaseItem item = ApiService.GetItemById(QueryString["id"]);
|
||||
|
||||
// Get the user
|
||||
User user = ApiService.GetUserById(QueryString["userid"], true);
|
||||
|
||||
// Get the user data for this item
|
||||
UserItemData data = item.GetUserData(user, true);
|
||||
|
||||
// Set favorite status
|
||||
data.IsFavorite = QueryString["isfavorite"] == "1";
|
||||
|
||||
return Task.FromResult(ApiService.GetDtoUserItemData(data));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a single genre
|
||||
/// </summary>
|
||||
[Export(typeof(BaseHandler))]
|
||||
public class GenreHandler : BaseSerializationHandler<IbnItem>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("genre", request);
|
||||
}
|
||||
|
||||
protected override Task<IbnItem> GetObjectToSerialize()
|
||||
{
|
||||
var parent = ApiService.GetItemById(QueryString["id"]) as Folder;
|
||||
var user = ApiService.GetUserById(QueryString["userid"], true);
|
||||
|
||||
string name = QueryString["name"];
|
||||
|
||||
return GetGenre(parent, user, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a Genre
|
||||
/// </summary>
|
||||
private async Task<IbnItem> GetGenre(Folder parent, User user, string name)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
// Get all the allowed recursive children
|
||||
IEnumerable<BaseItem> allItems = parent.GetRecursiveChildren(user);
|
||||
|
||||
foreach (var item in allItems)
|
||||
{
|
||||
if (item.Genres != null && item.Genres.Any(s => s.Equals(name, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the original entity so that we can also supply the PrimaryImagePath
|
||||
return ApiService.GetIbnItem(await Kernel.Instance.ItemController.GetGenre(name).ConfigureAwait(false), count);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
[Export(typeof(BaseHandler))]
|
||||
public class GenresHandler : BaseSerializationHandler<IbnItem[]>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("genres", request);
|
||||
}
|
||||
|
||||
protected override Task<IbnItem[]> GetObjectToSerialize()
|
||||
{
|
||||
var parent = ApiService.GetItemById(QueryString["id"]) as Folder;
|
||||
User user = ApiService.GetUserById(QueryString["userid"], true);
|
||||
|
||||
return GetAllGenres(parent, user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all genres from all recursive children of a folder
|
||||
/// The CategoryInfo class is used to keep track of the number of times each genres appears
|
||||
/// </summary>
|
||||
private async Task<IbnItem[]> GetAllGenres(Folder parent, User user)
|
||||
{
|
||||
var data = new Dictionary<string, int>();
|
||||
|
||||
// Get all the allowed recursive children
|
||||
IEnumerable<BaseItem> allItems = parent.GetRecursiveChildren(user);
|
||||
|
||||
foreach (var item in allItems)
|
||||
{
|
||||
// Add each genre from the item to the data dictionary
|
||||
// If the genre already exists, increment the count
|
||||
if (item.Genres == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (string val in item.Genres)
|
||||
{
|
||||
if (!data.ContainsKey(val))
|
||||
{
|
||||
data.Add(val, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
data[val]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the Genre objects
|
||||
Genre[] entities = await Task.WhenAll(data.Keys.Select(key => Kernel.Instance.ItemController.GetGenre(key))).ConfigureAwait(false);
|
||||
|
||||
// Convert to an array of IBNItem
|
||||
var items = new IbnItem[entities.Length];
|
||||
|
||||
for (int i = 0; i < entities.Length; i++)
|
||||
{
|
||||
Genre e = entities[i];
|
||||
|
||||
items[i] = ApiService.GetIbnItem(e, data[e.Name]);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,224 +0,0 @@
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
[Export(typeof(BaseHandler))]
|
||||
public class ImageHandler : BaseHandler
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("image", request);
|
||||
}
|
||||
|
||||
private string _imagePath;
|
||||
|
||||
private async Task<string> GetImagePath()
|
||||
{
|
||||
_imagePath = _imagePath ?? await DiscoverImagePath();
|
||||
|
||||
return _imagePath;
|
||||
}
|
||||
|
||||
private BaseEntity _sourceEntity;
|
||||
|
||||
private async Task<BaseEntity> GetSourceEntity()
|
||||
{
|
||||
if (_sourceEntity == null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(QueryString["personname"]))
|
||||
{
|
||||
_sourceEntity =
|
||||
await Kernel.Instance.ItemController.GetPerson(QueryString["personname"]).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
else if (!string.IsNullOrEmpty(QueryString["genre"]))
|
||||
{
|
||||
_sourceEntity =
|
||||
await Kernel.Instance.ItemController.GetGenre(QueryString["genre"]).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
else if (!string.IsNullOrEmpty(QueryString["year"]))
|
||||
{
|
||||
_sourceEntity =
|
||||
await
|
||||
Kernel.Instance.ItemController.GetYear(int.Parse(QueryString["year"])).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
else if (!string.IsNullOrEmpty(QueryString["studio"]))
|
||||
{
|
||||
_sourceEntity =
|
||||
await Kernel.Instance.ItemController.GetStudio(QueryString["studio"]).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
else if (!string.IsNullOrEmpty(QueryString["userid"]))
|
||||
{
|
||||
_sourceEntity = ApiService.GetUserById(QueryString["userid"], false);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
_sourceEntity = ApiService.GetItemById(QueryString["id"]);
|
||||
}
|
||||
}
|
||||
|
||||
return _sourceEntity;
|
||||
}
|
||||
|
||||
private async Task<string> DiscoverImagePath()
|
||||
{
|
||||
var entity = await GetSourceEntity().ConfigureAwait(false);
|
||||
|
||||
return ImageProcessor.GetImagePath(entity, ImageType, ImageIndex);
|
||||
}
|
||||
|
||||
protected async override Task<ResponseInfo> GetResponseInfo()
|
||||
{
|
||||
string path = await GetImagePath().ConfigureAwait(false);
|
||||
|
||||
ResponseInfo info = new ResponseInfo
|
||||
{
|
||||
CacheDuration = TimeSpan.FromDays(365),
|
||||
ContentType = MimeTypes.GetMimeType(path)
|
||||
};
|
||||
|
||||
DateTime? date = File.GetLastWriteTimeUtc(path);
|
||||
|
||||
// If the file does not exist it will return jan 1, 1601
|
||||
// http://msdn.microsoft.com/en-us/library/system.io.file.getlastwritetimeutc.aspx
|
||||
if (date.Value.Year == 1601)
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
info.StatusCode = 404;
|
||||
date = null;
|
||||
}
|
||||
}
|
||||
|
||||
info.DateLastModified = date;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
private int ImageIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
string val = QueryString["index"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
private int? Height
|
||||
{
|
||||
get
|
||||
{
|
||||
string val = QueryString["height"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
private int? Width
|
||||
{
|
||||
get
|
||||
{
|
||||
string val = QueryString["width"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
private int? MaxHeight
|
||||
{
|
||||
get
|
||||
{
|
||||
string val = QueryString["maxheight"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
private int? MaxWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
string val = QueryString["maxwidth"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
private int? Quality
|
||||
{
|
||||
get
|
||||
{
|
||||
string val = QueryString["quality"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
private ImageType ImageType
|
||||
{
|
||||
get
|
||||
{
|
||||
string imageType = QueryString["type"];
|
||||
|
||||
if (string.IsNullOrEmpty(imageType))
|
||||
{
|
||||
return ImageType.Primary;
|
||||
}
|
||||
|
||||
return (ImageType)Enum.Parse(typeof(ImageType), imageType, true);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task WriteResponseToOutputStream(Stream stream)
|
||||
{
|
||||
var entity = await GetSourceEntity().ConfigureAwait(false);
|
||||
|
||||
ImageProcessor.ProcessImage(entity, ImageType, ImageIndex, stream, Width, Height, MaxWidth, MaxHeight, Quality);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a handler to retrieve a single item
|
||||
/// </summary>
|
||||
[Export(typeof(BaseHandler))]
|
||||
public class ItemHandler : BaseSerializationHandler<DtoBaseItem>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("item", request);
|
||||
}
|
||||
|
||||
protected override Task<DtoBaseItem> GetObjectToSerialize()
|
||||
{
|
||||
User user = ApiService.GetUserById(QueryString["userid"], true);
|
||||
|
||||
BaseItem item = ApiService.GetItemById(QueryString["id"]);
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return ApiService.GetDtoBaseItem(item, user);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
[Export(typeof(BaseHandler))]
|
||||
public class ItemListHandler : BaseSerializationHandler<DtoBaseItem[]>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("itemlist", request);
|
||||
}
|
||||
|
||||
protected override Task<DtoBaseItem[]> GetObjectToSerialize()
|
||||
{
|
||||
User user = ApiService.GetUserById(QueryString["userid"], true);
|
||||
|
||||
return Task.WhenAll(GetItemsToSerialize(user).Select(i => ApiService.GetDtoBaseItem(i, user, includeChildren: false, includePeople: false)));
|
||||
}
|
||||
|
||||
private IEnumerable<BaseItem> GetItemsToSerialize(User user)
|
||||
{
|
||||
var parent = ApiService.GetItemById(ItemId) as Folder;
|
||||
|
||||
if (ListType.Equals("inprogressitems", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return parent.GetInProgressItems(user);
|
||||
}
|
||||
if (ListType.Equals("recentlyaddeditems", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return parent.GetRecentlyAddedItems(user);
|
||||
}
|
||||
if (ListType.Equals("recentlyaddedunplayeditems", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return parent.GetRecentlyAddedUnplayedItems(user);
|
||||
}
|
||||
if (ListType.Equals("itemswithgenre", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return parent.GetItemsWithGenre(QueryString["name"], user);
|
||||
}
|
||||
if (ListType.Equals("itemswithyear", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return parent.GetItemsWithYear(int.Parse(QueryString["year"]), user);
|
||||
}
|
||||
if (ListType.Equals("itemswithstudio", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return parent.GetItemsWithStudio(QueryString["name"], user);
|
||||
}
|
||||
if (ListType.Equals("itemswithperson", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return parent.GetItemsWithPerson(QueryString["name"], null, user);
|
||||
}
|
||||
if (ListType.Equals("favorites", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return parent.GetFavoriteItems(user);
|
||||
}
|
||||
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
protected string ItemId
|
||||
{
|
||||
get
|
||||
{
|
||||
return QueryString["id"];
|
||||
}
|
||||
}
|
||||
|
||||
private string ListType
|
||||
{
|
||||
get
|
||||
{
|
||||
return QueryString["listtype"] ?? string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// This handler retrieves special features for movies
|
||||
/// </summary>
|
||||
[Export(typeof(BaseHandler))]
|
||||
public class MovieSpecialFeaturesHandler : BaseSerializationHandler<DtoBaseItem[]>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("MovieSpecialFeatures", request);
|
||||
}
|
||||
|
||||
protected override Task<DtoBaseItem[]> GetObjectToSerialize()
|
||||
{
|
||||
User user = ApiService.GetUserById(QueryString["userid"], true);
|
||||
|
||||
var movie = ApiService.GetItemById(ItemId) as Movie;
|
||||
|
||||
// If none
|
||||
if (movie.SpecialFeatures == null)
|
||||
{
|
||||
return Task.FromResult(new DtoBaseItem[] { });
|
||||
}
|
||||
|
||||
return Task.WhenAll(movie.SpecialFeatures.Select(i => ApiService.GetDtoBaseItem(i, user, includeChildren: false)));
|
||||
}
|
||||
|
||||
protected string ItemId
|
||||
{
|
||||
get
|
||||
{
|
||||
return QueryString["id"];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a single Person
|
||||
/// </summary>
|
||||
[Export(typeof(BaseHandler))]
|
||||
public class PersonHandler : BaseSerializationHandler<IbnItem>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("person", request);
|
||||
}
|
||||
|
||||
protected override Task<IbnItem> GetObjectToSerialize()
|
||||
{
|
||||
var parent = ApiService.GetItemById(QueryString["id"]) as Folder;
|
||||
var user = ApiService.GetUserById(QueryString["userid"], true);
|
||||
|
||||
string name = QueryString["name"];
|
||||
|
||||
return GetPerson(parent, user, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a Person
|
||||
/// </summary>
|
||||
private async Task<IbnItem> GetPerson(Folder parent, User user, string name)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
// Get all the allowed recursive children
|
||||
IEnumerable<BaseItem> allItems = parent.GetRecursiveChildren(user);
|
||||
|
||||
foreach (var item in allItems)
|
||||
{
|
||||
if (item.People != null && item.People.ContainsKey(name))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the original entity so that we can also supply the PrimaryImagePath
|
||||
return ApiService.GetIbnItem(await Kernel.Instance.ItemController.GetPerson(name).ConfigureAwait(false), count);
|
||||
}
|
||||
}
|
||||
}
|
112
MediaBrowser.Api/HttpHandlers/PlaybackCheckInHandler.cs
Normal file
112
MediaBrowser.Api/HttpHandlers/PlaybackCheckInHandler.cs
Normal file
@ -0,0 +1,112 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Connectivity;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a handler to set played status for an item
|
||||
/// </summary>
|
||||
[Export(typeof(IHttpServerHandler))]
|
||||
public class PlaybackCheckInHandler : BaseSerializationHandler<Kernel, DtoUserItemData>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the object to serialize.
|
||||
/// </summary>
|
||||
/// <returns>Task{DtoUserItemData}.</returns>
|
||||
protected override async Task<DtoUserItemData> GetObjectToSerialize()
|
||||
{
|
||||
// Get the user
|
||||
var user = await this.GetCurrentUser().ConfigureAwait(false);
|
||||
|
||||
var clientType = ClientType.Other;
|
||||
|
||||
if (!string.IsNullOrEmpty(QueryString["client"]))
|
||||
{
|
||||
ClientType type;
|
||||
|
||||
if (Enum.TryParse(QueryString["client"], true, out type))
|
||||
{
|
||||
clientType = type;
|
||||
}
|
||||
}
|
||||
|
||||
var device = QueryString["device"];
|
||||
|
||||
// Get the item
|
||||
var item = DtoBuilder.GetItemByClientId(QueryString["id"], user.Id);
|
||||
|
||||
// Playback start check-in
|
||||
if (QueryString["type"].Equals("start", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Kernel.UserDataManager.OnPlaybackStart(user, item, clientType, device);
|
||||
}
|
||||
else
|
||||
{
|
||||
long? positionTicks = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(QueryString["positionTicks"]))
|
||||
{
|
||||
positionTicks = long.Parse(QueryString["positionTicks"]);
|
||||
}
|
||||
|
||||
// Progress check-ins require position ticks
|
||||
if (QueryString["type"].Equals("progress", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await Kernel.UserDataManager.OnPlaybackProgress(user, item, positionTicks, clientType, device).ConfigureAwait(false);
|
||||
}
|
||||
else if (QueryString["type"].Equals("stopped", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await Kernel.UserDataManager.OnPlaybackStopped(user, item, positionTicks, clientType, device).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
var data = item.GetUserData(user, true);
|
||||
|
||||
return DtoBuilder.GetDtoUserItemData(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current user.
|
||||
/// </summary>
|
||||
/// <returns>User.</returns>
|
||||
/// <exception cref="System.UnauthorizedAccessException"></exception>
|
||||
public async Task<User> GetCurrentUser()
|
||||
{
|
||||
var handler = this;
|
||||
var id = handler.QueryString["userid"];
|
||||
|
||||
var user = ApiService.GetUserById(id);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
throw new UnauthorizedAccessException(string.Format("User with Id {0} does not exist", id));
|
||||
}
|
||||
|
||||
var clientType = ClientType.Other;
|
||||
|
||||
if (!string.IsNullOrEmpty(handler.QueryString["client"]))
|
||||
{
|
||||
ClientType type;
|
||||
|
||||
if (Enum.TryParse(handler.QueryString["client"], true, out type))
|
||||
{
|
||||
clientType = type;
|
||||
}
|
||||
}
|
||||
|
||||
var device = handler.QueryString["device"];
|
||||
|
||||
await Controller.Kernel.Instance.UserManager.LogUserActivity(user, clientType, device).ConfigureAwait(false);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a handler to set played status for an item
|
||||
/// </summary>
|
||||
[Export(typeof(BaseHandler))]
|
||||
public class PlayedStatusHandler : BaseSerializationHandler<DtoUserItemData>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("PlayedStatus", request);
|
||||
}
|
||||
|
||||
protected override Task<DtoUserItemData> GetObjectToSerialize()
|
||||
{
|
||||
// Get the item
|
||||
BaseItem item = ApiService.GetItemById(QueryString["id"]);
|
||||
|
||||
// Get the user
|
||||
User user = ApiService.GetUserById(QueryString["userid"], true);
|
||||
|
||||
bool wasPlayed = QueryString["played"] == "1";
|
||||
|
||||
item.SetPlayedStatus(user, wasPlayed);
|
||||
|
||||
UserItemData data = item.GetUserData(user, true);
|
||||
|
||||
return Task.FromResult(ApiService.GetDtoUserItemData(data));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
[Export(typeof(BaseHandler))]
|
||||
class PluginAssemblyHandler : BaseHandler
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("pluginassembly", request);
|
||||
}
|
||||
|
||||
protected override Task<ResponseInfo> GetResponseInfo()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override Task WriteResponseToOutputStream(Stream stream)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override Task ProcessRequest(HttpListenerContext ctx)
|
||||
{
|
||||
string filename = ctx.Request.QueryString["assemblyfilename"];
|
||||
|
||||
string path = Path.Combine(Kernel.Instance.ApplicationPaths.PluginsPath, filename);
|
||||
|
||||
return new StaticFileHandler { Path = path }.ProcessRequest(ctx);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Model.Plugins;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
[Export(typeof(BaseHandler))]
|
||||
public class PluginConfigurationHandler : BaseSerializationHandler<BasePluginConfiguration>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("pluginconfiguration", request);
|
||||
}
|
||||
|
||||
private BasePlugin _plugin;
|
||||
private BasePlugin Plugin
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_plugin == null)
|
||||
{
|
||||
string name = QueryString["assemblyfilename"];
|
||||
|
||||
_plugin = Kernel.Instance.Plugins.First(p => p.AssemblyFileName.Equals(name, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
return _plugin;
|
||||
}
|
||||
}
|
||||
|
||||
protected override Task<BasePluginConfiguration> GetObjectToSerialize()
|
||||
{
|
||||
return Task.FromResult(Plugin.Configuration);
|
||||
}
|
||||
|
||||
protected override async Task<ResponseInfo> GetResponseInfo()
|
||||
{
|
||||
var info = await base.GetResponseInfo().ConfigureAwait(false);
|
||||
|
||||
info.DateLastModified = Plugin.ConfigurationDateLastModified;
|
||||
|
||||
info.CacheDuration = TimeSpan.FromDays(7);
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides information about installed plugins
|
||||
/// </summary>
|
||||
[Export(typeof(BaseHandler))]
|
||||
public class PluginsHandler : BaseSerializationHandler<IEnumerable<PluginInfo>>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("plugins", request);
|
||||
}
|
||||
|
||||
protected override Task<IEnumerable<PluginInfo>> GetObjectToSerialize()
|
||||
{
|
||||
var plugins = Kernel.Instance.Plugins.Select(p => new PluginInfo
|
||||
{
|
||||
Name = p.Name,
|
||||
Enabled = p.Enabled,
|
||||
DownloadToUI = p.DownloadToUi,
|
||||
Version = p.Version.ToString(),
|
||||
AssemblyFileName = p.AssemblyFileName,
|
||||
ConfigurationDateLastModified = p.ConfigurationDateLastModified
|
||||
});
|
||||
|
||||
return Task.FromResult(plugins);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
[Export(typeof(BaseHandler))]
|
||||
class ServerConfigurationHandler : BaseSerializationHandler<ServerConfiguration>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("serverconfiguration", request);
|
||||
}
|
||||
|
||||
protected override Task<ServerConfiguration> GetObjectToSerialize()
|
||||
{
|
||||
return Task.FromResult(Kernel.Instance.Configuration);
|
||||
}
|
||||
|
||||
protected override async Task<ResponseInfo> GetResponseInfo()
|
||||
{
|
||||
var info = await base.GetResponseInfo().ConfigureAwait(false);
|
||||
|
||||
info.DateLastModified =
|
||||
File.GetLastWriteTimeUtc(Kernel.Instance.ApplicationPaths.SystemConfigurationFilePath);
|
||||
|
||||
info.CacheDuration = TimeSpan.FromDays(7);
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a single studio
|
||||
/// </summary>
|
||||
[Export(typeof(BaseHandler))]
|
||||
public class StudioHandler : BaseSerializationHandler<IbnItem>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("studio", request);
|
||||
}
|
||||
|
||||
protected override Task<IbnItem> GetObjectToSerialize()
|
||||
{
|
||||
var parent = ApiService.GetItemById(QueryString["id"]) as Folder;
|
||||
var user = ApiService.GetUserById(QueryString["userid"], true);
|
||||
|
||||
string name = QueryString["name"];
|
||||
|
||||
return GetStudio(parent, user, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a Studio
|
||||
/// </summary>
|
||||
private async Task<IbnItem> GetStudio(Folder parent, User user, string name)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
// Get all the allowed recursive children
|
||||
IEnumerable<BaseItem> allItems = parent.GetRecursiveChildren(user);
|
||||
|
||||
foreach (var item in allItems)
|
||||
{
|
||||
if (item.Studios != null && item.Studios.Any(s => s.Equals(name, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the original entity so that we can also supply the PrimaryImagePath
|
||||
return ApiService.GetIbnItem(await Kernel.Instance.ItemController.GetStudio(name).ConfigureAwait(false), count);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
[Export(typeof(BaseHandler))]
|
||||
public class StudiosHandler : BaseSerializationHandler<IbnItem[]>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("studios", request);
|
||||
}
|
||||
|
||||
protected override Task<IbnItem[]> GetObjectToSerialize()
|
||||
{
|
||||
var parent = ApiService.GetItemById(QueryString["id"]) as Folder;
|
||||
var user = ApiService.GetUserById(QueryString["userid"], true);
|
||||
|
||||
return GetAllStudios(parent, user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all studios from all recursive children of a folder
|
||||
/// The CategoryInfo class is used to keep track of the number of times each studio appears
|
||||
/// </summary>
|
||||
private async Task<IbnItem[]> GetAllStudios(Folder parent, User user)
|
||||
{
|
||||
var data = new Dictionary<string, int>();
|
||||
|
||||
// Get all the allowed recursive children
|
||||
IEnumerable<BaseItem> allItems = parent.GetRecursiveChildren(user);
|
||||
|
||||
foreach (var item in allItems)
|
||||
{
|
||||
// Add each studio from the item to the data dictionary
|
||||
// If the studio already exists, increment the count
|
||||
if (item.Studios == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (string val in item.Studios)
|
||||
{
|
||||
if (!data.ContainsKey(val))
|
||||
{
|
||||
data.Add(val, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
data[val]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the Studio objects
|
||||
Studio[] entities = await Task.WhenAll(data.Keys.Select(key => Kernel.Instance.ItemController.GetStudio(key))).ConfigureAwait(false);
|
||||
|
||||
// Convert to an array of IBNItem
|
||||
var items = new IbnItem[entities.Length];
|
||||
|
||||
for (int i = 0; i < entities.Length; i++)
|
||||
{
|
||||
Studio e = entities[i];
|
||||
|
||||
items[i] = ApiService.GetIbnItem(e, data[e.Name]);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
||||
}
|
263
MediaBrowser.Api/HttpHandlers/UpdateMediaLibraryHandler.cs
Normal file
263
MediaBrowser.Api/HttpHandlers/UpdateMediaLibraryHandler.cs
Normal file
@ -0,0 +1,263 @@
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Makes changes to the user's media library
|
||||
/// </summary>
|
||||
[Export(typeof(IHttpServerHandler))]
|
||||
public class UpdateMediaLibraryHandler : BaseActionHandler<Kernel>
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes the action.
|
||||
/// </summary>
|
||||
/// <returns>Task.</returns>
|
||||
/// <exception cref="System.NotImplementedException"></exception>
|
||||
protected override Task ExecuteAction()
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var action = QueryString["action"];
|
||||
|
||||
if (string.IsNullOrEmpty(action))
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
User user = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(QueryString["userId"]))
|
||||
{
|
||||
user = ApiService.GetUserById(QueryString["userId"]);
|
||||
}
|
||||
|
||||
if (action.Equals("AddVirtualFolder", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
AddVirtualFolder(Uri.UnescapeDataString(QueryString["name"]), user);
|
||||
}
|
||||
|
||||
if (action.Equals("RemoveVirtualFolder", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
RemoveVirtualFolder(QueryString["name"], user);
|
||||
}
|
||||
|
||||
if (action.Equals("RenameVirtualFolder", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
RenameVirtualFolder(QueryString["name"], QueryString["newName"], user);
|
||||
}
|
||||
|
||||
if (action.Equals("RemoveMediaPath", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
RemoveMediaPath(QueryString["virtualFolderName"], QueryString["mediaPath"], user);
|
||||
}
|
||||
|
||||
if (action.Equals("AddMediaPath", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
AddMediaPath(QueryString["virtualFolderName"], QueryString["mediaPath"], user);
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a virtual folder to either the default view or a user view
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
private void AddVirtualFolder(string name, User user)
|
||||
{
|
||||
name = FileSystem.GetValidFilename(name);
|
||||
|
||||
var rootFolderPath = user != null ? user.RootFolderPath : Kernel.ApplicationPaths.DefaultUserViewsPath;
|
||||
var virtualFolderPath = Path.Combine(rootFolderPath, name);
|
||||
|
||||
if (Directory.Exists(virtualFolderPath))
|
||||
{
|
||||
throw new ArgumentException("There is already a media collection with the name " + name + ".");
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(virtualFolderPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an additional mediaPath to an existing virtual folder, within either the default view or a user view
|
||||
/// </summary>
|
||||
/// <param name="virtualFolderName">Name of the virtual folder.</param>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
private void AddMediaPath(string virtualFolderName, string path, User user)
|
||||
{
|
||||
if (!Path.IsPathRooted(path))
|
||||
{
|
||||
throw new ArgumentException("The path is not valid.");
|
||||
}
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
throw new DirectoryNotFoundException("The path does not exist.");
|
||||
}
|
||||
|
||||
// Strip off trailing slash, but not on drives
|
||||
path = path.TrimEnd(Path.DirectorySeparatorChar);
|
||||
if (path.EndsWith(":", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
path += Path.DirectorySeparatorChar;
|
||||
}
|
||||
|
||||
var rootFolderPath = user != null ? user.RootFolderPath : Kernel.ApplicationPaths.DefaultUserViewsPath;
|
||||
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
|
||||
|
||||
ValidateNewMediaPath(rootFolderPath, path);
|
||||
|
||||
var shortcutFilename = Path.GetFileNameWithoutExtension(path);
|
||||
|
||||
var lnk = Path.Combine(virtualFolderPath, shortcutFilename + ".lnk");
|
||||
|
||||
while (File.Exists(lnk))
|
||||
{
|
||||
shortcutFilename += "1";
|
||||
lnk = Path.Combine(virtualFolderPath, shortcutFilename + ".lnk");
|
||||
}
|
||||
|
||||
FileSystem.CreateShortcut(lnk, path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that a new media path can be added
|
||||
/// </summary>
|
||||
/// <param name="currentViewRootFolderPath">The current view root folder path.</param>
|
||||
/// <param name="mediaPath">The media path.</param>
|
||||
private void ValidateNewMediaPath(string currentViewRootFolderPath, string mediaPath)
|
||||
{
|
||||
var duplicate = Directory.EnumerateFiles(Kernel.ApplicationPaths.RootFolderPath, "*.lnk", SearchOption.AllDirectories)
|
||||
.Select(FileSystem.ResolveShortcut)
|
||||
.FirstOrDefault(p => !IsNewPathValid(mediaPath, p));
|
||||
|
||||
if (!string.IsNullOrEmpty(duplicate))
|
||||
{
|
||||
throw new ArgumentException(string.Format("The path cannot be added to the library because {0} already exists.", duplicate));
|
||||
}
|
||||
|
||||
// Make sure the current root folder doesn't already have a shortcut to the same path
|
||||
duplicate = Directory.EnumerateFiles(currentViewRootFolderPath, "*.lnk", SearchOption.AllDirectories)
|
||||
.Select(FileSystem.ResolveShortcut)
|
||||
.FirstOrDefault(p => mediaPath.Equals(p, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (!string.IsNullOrEmpty(duplicate))
|
||||
{
|
||||
throw new ArgumentException(string.Format("The path {0} already exists in the library", mediaPath));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that a new path can be added based on an existing path
|
||||
/// </summary>
|
||||
/// <param name="newPath">The new path.</param>
|
||||
/// <param name="existingPath">The existing path.</param>
|
||||
/// <returns><c>true</c> if [is new path valid] [the specified new path]; otherwise, <c>false</c>.</returns>
|
||||
private bool IsNewPathValid(string newPath, string existingPath)
|
||||
{
|
||||
// Example: D:\Movies is the existing path
|
||||
// D:\ cannot be added
|
||||
// Neither can D:\Movies\Kids
|
||||
// A D:\Movies duplicate is ok here since that will be caught later
|
||||
|
||||
if (newPath.Equals(existingPath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validate the D:\Movies\Kids scenario
|
||||
if (newPath.StartsWith(existingPath.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate the D:\ scenario
|
||||
if (existingPath.StartsWith(newPath.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renames a virtual folder within either the default view or a user view
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="newName">The new name.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
private void RenameVirtualFolder(string name, string newName, User user)
|
||||
{
|
||||
var rootFolderPath = user != null ? user.RootFolderPath : Kernel.ApplicationPaths.DefaultUserViewsPath;
|
||||
|
||||
var currentPath = Path.Combine(rootFolderPath, name);
|
||||
var newPath = Path.Combine(rootFolderPath, newName);
|
||||
|
||||
if (!Directory.Exists(currentPath))
|
||||
{
|
||||
throw new DirectoryNotFoundException("The media collection does not exist");
|
||||
}
|
||||
|
||||
if (Directory.Exists(newPath))
|
||||
{
|
||||
throw new ArgumentException("There is already a media collection with the name " + newPath + ".");
|
||||
}
|
||||
|
||||
Directory.Move(currentPath, newPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a virtual folder from either the default view or a user view
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
private void RemoveVirtualFolder(string name, User user)
|
||||
{
|
||||
var rootFolderPath = user != null ? user.RootFolderPath : Kernel.ApplicationPaths.DefaultUserViewsPath;
|
||||
var path = Path.Combine(rootFolderPath, name);
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
throw new DirectoryNotFoundException("The media folder does not exist");
|
||||
}
|
||||
|
||||
Directory.Delete(path, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a shortcut from within a virtual folder, within either the default view or a user view
|
||||
/// </summary>
|
||||
/// <param name="virtualFolderName">Name of the virtual folder.</param>
|
||||
/// <param name="mediaPath">The media path.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
private void RemoveMediaPath(string virtualFolderName, string mediaPath, User user)
|
||||
{
|
||||
var rootFolderPath = user != null ? user.RootFolderPath : Kernel.ApplicationPaths.DefaultUserViewsPath;
|
||||
var path = Path.Combine(rootFolderPath, virtualFolderName);
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
throw new DirectoryNotFoundException("The media folder does not exist");
|
||||
}
|
||||
|
||||
var shortcut = Directory.EnumerateFiles(path, "*.lnk", SearchOption.AllDirectories).FirstOrDefault(f => FileSystem.ResolveShortcut(f).Equals(mediaPath, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (string.IsNullOrEmpty(shortcut))
|
||||
{
|
||||
throw new DirectoryNotFoundException("The media folder does not exist");
|
||||
}
|
||||
File.Delete(shortcut);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Authentication;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
[Export(typeof(BaseHandler))]
|
||||
class UserAuthenticationHandler : BaseSerializationHandler<AuthenticationResult>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("UserAuthentication", request);
|
||||
}
|
||||
|
||||
protected override async Task<AuthenticationResult> GetObjectToSerialize()
|
||||
{
|
||||
string userId = await GetFormValue("userid").ConfigureAwait(false);
|
||||
User user = ApiService.GetUserById(userId, false);
|
||||
|
||||
string password = await GetFormValue("password").ConfigureAwait(false);
|
||||
|
||||
return Kernel.Instance.AuthenticateUser(user, password);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
[Export(typeof(BaseHandler))]
|
||||
class UserHandler : BaseSerializationHandler<DtoUser>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("user", request);
|
||||
}
|
||||
|
||||
protected override Task<DtoUser> GetObjectToSerialize()
|
||||
{
|
||||
string id = QueryString["id"];
|
||||
|
||||
User user = string.IsNullOrEmpty(id) ? ApiService.GetDefaultUser(false) : ApiService.GetUserById(id, false);
|
||||
|
||||
DtoUser dto = ApiService.GetDtoUser(user);
|
||||
|
||||
return Task.FromResult(dto);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a handler to set a user's rating for an item
|
||||
/// </summary>
|
||||
[Export(typeof(BaseHandler))]
|
||||
public class UserItemRatingHandler : BaseSerializationHandler<DtoUserItemData>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("UserItemRating", request);
|
||||
}
|
||||
|
||||
protected override Task<DtoUserItemData> GetObjectToSerialize()
|
||||
{
|
||||
// Get the item
|
||||
BaseItem item = ApiService.GetItemById(QueryString["id"]);
|
||||
|
||||
// Get the user
|
||||
User user = ApiService.GetUserById(QueryString["userid"], true);
|
||||
|
||||
// Get the user data for this item
|
||||
UserItemData data = item.GetUserData(user, true);
|
||||
|
||||
// If clearing the rating, set it to null
|
||||
if (QueryString["clear"] == "1")
|
||||
{
|
||||
data.Rating = null;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
data.Likes = QueryString["likes"] == "1";
|
||||
}
|
||||
|
||||
return Task.FromResult(ApiService.GetDtoUserItemData(data));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
[Export(typeof(BaseHandler))]
|
||||
class UsersHandler : BaseSerializationHandler<IEnumerable<DtoUser>>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("users", request);
|
||||
}
|
||||
|
||||
protected override Task<IEnumerable<DtoUser>> GetObjectToSerialize()
|
||||
{
|
||||
return Task.FromResult(Kernel.Instance.Users.Select(u => ApiService.GetDtoUser(u)));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,424 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Supported output formats: mkv,m4v,mp4,asf,wmv,mov,webm,ogv,3gp,avi,ts,flv
|
||||
/// </summary>
|
||||
[Export(typeof(BaseHandler))]
|
||||
class VideoHandler : BaseMediaHandler<Video, VideoOutputFormats>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("video", request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We can output these files directly, but we can't encode them
|
||||
/// </summary>
|
||||
protected override IEnumerable<VideoOutputFormats> UnsupportedOutputEncodingFormats
|
||||
{
|
||||
get
|
||||
{
|
||||
// mp4, 3gp, mov - muxer does not support non-seekable output
|
||||
// avi, mov, mkv, m4v - can't stream these when encoding. the player will try to download them completely before starting playback.
|
||||
// wmv - can't seem to figure out the output format name
|
||||
return new VideoOutputFormats[] { VideoOutputFormats.Mp4, VideoOutputFormats.ThreeGp, VideoOutputFormats.M4V, VideoOutputFormats.Mkv, VideoOutputFormats.Avi, VideoOutputFormats.Mov, VideoOutputFormats.Wmv };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether or not we can just output the original file directly
|
||||
/// </summary>
|
||||
protected override bool RequiresConversion()
|
||||
{
|
||||
if (base.RequiresConversion())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// See if the video requires conversion
|
||||
if (RequiresVideoConversion())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// See if the audio requires conversion
|
||||
AudioStream audioStream = (LibraryItem.AudioStreams ?? new List<AudioStream>()).FirstOrDefault();
|
||||
|
||||
if (audioStream != null)
|
||||
{
|
||||
if (RequiresAudioConversion(audioStream))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Yay
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translates the output file extension to the format param that follows "-f" on the ffmpeg command line
|
||||
/// </summary>
|
||||
private string GetFfMpegOutputFormat(VideoOutputFormats outputFormat)
|
||||
{
|
||||
if (outputFormat == VideoOutputFormats.Mkv)
|
||||
{
|
||||
return "matroska";
|
||||
}
|
||||
if (outputFormat == VideoOutputFormats.Ts)
|
||||
{
|
||||
return "mpegts";
|
||||
}
|
||||
if (outputFormat == VideoOutputFormats.Ogv)
|
||||
{
|
||||
return "ogg";
|
||||
}
|
||||
|
||||
return outputFormat.ToString().ToLower();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates arguments to pass to ffmpeg
|
||||
/// </summary>
|
||||
protected override string GetCommandLineArguments()
|
||||
{
|
||||
VideoOutputFormats outputFormat = GetConversionOutputFormat();
|
||||
|
||||
return string.Format("-i \"{0}\" -threads 0 {1} {2} -f {3} -",
|
||||
LibraryItem.Path,
|
||||
GetVideoArguments(outputFormat),
|
||||
GetAudioArguments(outputFormat),
|
||||
GetFfMpegOutputFormat(outputFormat)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets video arguments to pass to ffmpeg
|
||||
/// </summary>
|
||||
private string GetVideoArguments(VideoOutputFormats outputFormat)
|
||||
{
|
||||
// Get the output codec name
|
||||
string codec = GetVideoCodec(outputFormat);
|
||||
|
||||
string args = "-vcodec " + codec;
|
||||
|
||||
// If we're encoding video, add additional params
|
||||
if (!codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Add resolution params, if specified
|
||||
if (Width.HasValue || Height.HasValue || MaxHeight.HasValue || MaxWidth.HasValue)
|
||||
{
|
||||
Size size = DrawingUtils.Resize(LibraryItem.Width, LibraryItem.Height, Width, Height, MaxWidth, MaxHeight);
|
||||
|
||||
args += string.Format(" -s {0}x{1}", size.Width, size.Height);
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets audio arguments to pass to ffmpeg
|
||||
/// </summary>
|
||||
private string GetAudioArguments(VideoOutputFormats outputFormat)
|
||||
{
|
||||
AudioStream audioStream = (LibraryItem.AudioStreams ?? new List<AudioStream>()).FirstOrDefault();
|
||||
|
||||
// If the video doesn't have an audio stream, return empty
|
||||
if (audioStream == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// Get the output codec name
|
||||
string codec = GetAudioCodec(audioStream, outputFormat);
|
||||
|
||||
string args = "-acodec " + codec;
|
||||
|
||||
// If we're encoding audio, add additional params
|
||||
if (!codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Add the number of audio channels
|
||||
int? channels = GetNumAudioChannelsParam(codec, audioStream.Channels);
|
||||
|
||||
if (channels.HasValue)
|
||||
{
|
||||
args += " -ac " + channels.Value;
|
||||
}
|
||||
|
||||
// Add the audio sample rate
|
||||
int? sampleRate = GetSampleRateParam(audioStream.SampleRate);
|
||||
|
||||
if (sampleRate.HasValue)
|
||||
{
|
||||
args += " -ar " + sampleRate.Value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the output video codec
|
||||
/// </summary>
|
||||
private string GetVideoCodec(VideoOutputFormats outputFormat)
|
||||
{
|
||||
// Some output containers require specific codecs
|
||||
|
||||
if (outputFormat == VideoOutputFormats.Webm)
|
||||
{
|
||||
// Per webm specification, it must be vpx
|
||||
return "libvpx";
|
||||
}
|
||||
if (outputFormat == VideoOutputFormats.Asf)
|
||||
{
|
||||
return "wmv2";
|
||||
}
|
||||
if (outputFormat == VideoOutputFormats.Wmv)
|
||||
{
|
||||
return "wmv2";
|
||||
}
|
||||
if (outputFormat == VideoOutputFormats.Ogv)
|
||||
{
|
||||
return "libtheora";
|
||||
}
|
||||
|
||||
// Skip encoding when possible
|
||||
if (!RequiresVideoConversion())
|
||||
{
|
||||
return "copy";
|
||||
}
|
||||
|
||||
return "libx264";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the output audio codec
|
||||
/// </summary>
|
||||
private string GetAudioCodec(AudioStream audioStream, VideoOutputFormats outputFormat)
|
||||
{
|
||||
// Some output containers require specific codecs
|
||||
|
||||
if (outputFormat == VideoOutputFormats.Webm)
|
||||
{
|
||||
// Per webm specification, it must be vorbis
|
||||
return "libvorbis";
|
||||
}
|
||||
if (outputFormat == VideoOutputFormats.Asf)
|
||||
{
|
||||
return "wmav2";
|
||||
}
|
||||
if (outputFormat == VideoOutputFormats.Wmv)
|
||||
{
|
||||
return "wmav2";
|
||||
}
|
||||
if (outputFormat == VideoOutputFormats.Ogv)
|
||||
{
|
||||
return "libvorbis";
|
||||
}
|
||||
|
||||
// Skip encoding when possible
|
||||
if (!RequiresAudioConversion(audioStream))
|
||||
{
|
||||
return "copy";
|
||||
}
|
||||
|
||||
return "libvo_aacenc";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of audio channels to specify on the command line
|
||||
/// </summary>
|
||||
private int? GetNumAudioChannelsParam(string audioCodec, int libraryItemChannels)
|
||||
{
|
||||
if (libraryItemChannels > 2)
|
||||
{
|
||||
if (audioCodec.Equals("libvo_aacenc"))
|
||||
{
|
||||
// libvo_aacenc currently only supports two channel output
|
||||
return 2;
|
||||
}
|
||||
if (audioCodec.Equals("wmav2"))
|
||||
{
|
||||
// wmav2 currently only supports two channel output
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
return GetNumAudioChannelsParam(libraryItemChannels);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the video stream requires encoding
|
||||
/// </summary>
|
||||
private bool RequiresVideoConversion()
|
||||
{
|
||||
// Check dimensions
|
||||
|
||||
// If a specific width is required, validate that
|
||||
if (Width.HasValue)
|
||||
{
|
||||
if (Width.Value != LibraryItem.Width)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If a specific height is required, validate that
|
||||
if (Height.HasValue)
|
||||
{
|
||||
if (Height.Value != LibraryItem.Height)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If a max width is required, validate that
|
||||
if (MaxWidth.HasValue)
|
||||
{
|
||||
if (MaxWidth.Value < LibraryItem.Width)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If a max height is required, validate that
|
||||
if (MaxHeight.HasValue)
|
||||
{
|
||||
if (MaxHeight.Value < LibraryItem.Height)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If the codec is already h264, don't encode
|
||||
if (LibraryItem.Codec.IndexOf("264", StringComparison.OrdinalIgnoreCase) != -1 || LibraryItem.Codec.IndexOf("avc", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the audio stream requires encoding
|
||||
/// </summary>
|
||||
private bool RequiresAudioConversion(AudioStream audio)
|
||||
{
|
||||
|
||||
// If the input stream has more audio channels than the client can handle, we need to encode
|
||||
if (AudioChannels.HasValue)
|
||||
{
|
||||
if (audio.Channels > AudioChannels.Value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Aac, ac-3 and mp3 are all pretty much universally supported. No need to encode them
|
||||
|
||||
if (audio.Codec.IndexOf("aac", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (audio.Codec.IndexOf("ac-3", StringComparison.OrdinalIgnoreCase) != -1 || audio.Codec.IndexOf("ac3", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (audio.Codec.IndexOf("mpeg", StringComparison.OrdinalIgnoreCase) != -1 || audio.Codec.IndexOf("mp3", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the fixed output video height, in pixels
|
||||
/// </summary>
|
||||
private int? Height
|
||||
{
|
||||
get
|
||||
{
|
||||
string val = QueryString["height"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the fixed output video width, in pixels
|
||||
/// </summary>
|
||||
private int? Width
|
||||
{
|
||||
get
|
||||
{
|
||||
string val = QueryString["width"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum output video height, in pixels
|
||||
/// </summary>
|
||||
private int? MaxHeight
|
||||
{
|
||||
get
|
||||
{
|
||||
string val = QueryString["maxheight"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum output video width, in pixels
|
||||
/// </summary>
|
||||
private int? MaxWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
string val = QueryString["maxwidth"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Model.Weather;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
[Export(typeof(BaseHandler))]
|
||||
class WeatherHandler : BaseSerializationHandler<WeatherInfo>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("weather", request);
|
||||
}
|
||||
|
||||
protected override Task<WeatherInfo> GetObjectToSerialize()
|
||||
{
|
||||
// If a specific zip code was requested on the query string, use that. Otherwise use the value from configuration
|
||||
|
||||
string zipCode = QueryString["zipcode"];
|
||||
|
||||
if (string.IsNullOrWhiteSpace(zipCode))
|
||||
{
|
||||
zipCode = Kernel.Instance.Configuration.WeatherZipCode;
|
||||
}
|
||||
|
||||
return Kernel.Instance.WeatherProviders.First().GetWeatherInfoAsync(zipCode);
|
||||
}
|
||||
|
||||
protected override async Task<ResponseInfo> GetResponseInfo()
|
||||
{
|
||||
var info = await base.GetResponseInfo().ConfigureAwait(false);
|
||||
|
||||
info.CacheDuration = TimeSpan.FromMinutes(15);
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a single year
|
||||
/// </summary>
|
||||
[Export(typeof(BaseHandler))]
|
||||
public class YearHandler : BaseSerializationHandler<IbnItem>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("year", request);
|
||||
}
|
||||
|
||||
protected override Task<IbnItem> GetObjectToSerialize()
|
||||
{
|
||||
var parent = ApiService.GetItemById(QueryString["id"]) as Folder;
|
||||
var user = ApiService.GetUserById(QueryString["userid"], true);
|
||||
|
||||
string year = QueryString["year"];
|
||||
|
||||
return GetYear(parent, user, int.Parse(year));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a Year
|
||||
/// </summary>
|
||||
private async Task<IbnItem> GetYear(Folder parent, User user, int year)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
// Get all the allowed recursive children
|
||||
IEnumerable<BaseItem> allItems = parent.GetRecursiveChildren(user);
|
||||
|
||||
foreach (var item in allItems)
|
||||
{
|
||||
if (item.ProductionYear.HasValue && item.ProductionYear.Value == year)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the original entity so that we can also supply the PrimaryImagePath
|
||||
return ApiService.GetIbnItem(await Kernel.Instance.ItemController.GetYear(year).ConfigureAwait(false), count);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.HttpHandlers
|
||||
{
|
||||
[Export(typeof(BaseHandler))]
|
||||
public class YearsHandler : BaseSerializationHandler<IbnItem[]>
|
||||
{
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("years", request);
|
||||
}
|
||||
|
||||
protected override Task<IbnItem[]> GetObjectToSerialize()
|
||||
{
|
||||
var parent = ApiService.GetItemById(QueryString["id"]) as Folder;
|
||||
User user = ApiService.GetUserById(QueryString["userid"], true);
|
||||
|
||||
return GetAllYears(parent, user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all years from all recursive children of a folder
|
||||
/// The CategoryInfo class is used to keep track of the number of times each year appears
|
||||
/// </summary>
|
||||
private async Task<IbnItem[]> GetAllYears(Folder parent, User user)
|
||||
{
|
||||
var data = new Dictionary<int, int>();
|
||||
|
||||
// Get all the allowed recursive children
|
||||
IEnumerable<BaseItem> allItems = parent.GetRecursiveChildren(user);
|
||||
|
||||
foreach (var item in allItems)
|
||||
{
|
||||
// Add the year from the item to the data dictionary
|
||||
// If the year already exists, increment the count
|
||||
if (item.ProductionYear == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!data.ContainsKey(item.ProductionYear.Value))
|
||||
{
|
||||
data.Add(item.ProductionYear.Value, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
data[item.ProductionYear.Value]++;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the Year objects
|
||||
Year[] entities = await Task.WhenAll(data.Keys.Select(key => Kernel.Instance.ItemController.GetYear(key))).ConfigureAwait(false);
|
||||
|
||||
// Convert to an array of IBNItem
|
||||
var items = new IbnItem[entities.Length];
|
||||
|
||||
for (int i = 0; i < entities.Length; i++)
|
||||
{
|
||||
Year e = entities[i];
|
||||
|
||||
items[i] = ApiService.GetIbnItem(e, data[int.Parse(e.Name)]);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
||||
}
|
54
MediaBrowser.Api/Images/ImageRequest.cs
Normal file
54
MediaBrowser.Api/Images/ImageRequest.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
||||
namespace MediaBrowser.Api.Images
|
||||
{
|
||||
/// <summary>
|
||||
/// Class ImageRequest
|
||||
/// </summary>
|
||||
public class ImageRequest : DeleteImageRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// The max width
|
||||
/// </summary>
|
||||
public int? MaxWidth;
|
||||
/// <summary>
|
||||
/// The max height
|
||||
/// </summary>
|
||||
public int? MaxHeight;
|
||||
/// <summary>
|
||||
/// The width
|
||||
/// </summary>
|
||||
public int? Width;
|
||||
/// <summary>
|
||||
/// The height
|
||||
/// </summary>
|
||||
public int? Height;
|
||||
/// <summary>
|
||||
/// Gets or sets the quality.
|
||||
/// </summary>
|
||||
/// <value>The quality.</value>
|
||||
public int? Quality { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the tag.
|
||||
/// </summary>
|
||||
/// <value>The tag.</value>
|
||||
public string Tag { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class DeleteImageRequest
|
||||
/// </summary>
|
||||
public class DeleteImageRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the image.
|
||||
/// </summary>
|
||||
/// <value>The type of the image.</value>
|
||||
public ImageType Type { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the index.
|
||||
/// </summary>
|
||||
/// <value>The index.</value>
|
||||
public int? Index { get; set; }
|
||||
}
|
||||
}
|
286
MediaBrowser.Api/Images/ImageService.cs
Normal file
286
MediaBrowser.Api/Images/ImageService.cs
Normal file
@ -0,0 +1,286 @@
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using ServiceStack.ServiceHost;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.Images
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetItemImage
|
||||
/// </summary>
|
||||
[Route("/Items/{Id}/Images/{Type}", "GET")]
|
||||
[Route("/Items/{Id}/Images/{Type}/{Index}", "GET")]
|
||||
public class GetItemImage : ImageRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetPersonImage
|
||||
/// </summary>
|
||||
[Route("/Persons/{Name}/Images/{Type}", "GET")]
|
||||
[Route("/Persons/{Name}/Images/{Type}/{Index}", "GET")]
|
||||
public class GetPersonImage : ImageRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetStudioImage
|
||||
/// </summary>
|
||||
[Route("/Studios/{Name}/Images/{Type}", "GET")]
|
||||
[Route("/Studios/{Name}/Images/{Type}/{Index}", "GET")]
|
||||
public class GetStudioImage : ImageRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetGenreImage
|
||||
/// </summary>
|
||||
[Route("/Genres/{Name}/Images/{Type}", "GET")]
|
||||
[Route("/Genres/{Name}/Images/{Type}/{Index}", "GET")]
|
||||
public class GetGenreImage : ImageRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetYearImage
|
||||
/// </summary>
|
||||
[Route("/Years/{Year}/Images/{Type}", "GET")]
|
||||
[Route("/Years/{Year}/Images/{Type}/{Index}", "GET")]
|
||||
public class GetYearImage : ImageRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the year.
|
||||
/// </summary>
|
||||
/// <value>The year.</value>
|
||||
public int Year { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetUserImage
|
||||
/// </summary>
|
||||
[Route("/Users/{Id}/Images/{Type}", "GET")]
|
||||
[Route("/Users/{Id}/Images/{Type}/{Index}", "GET")]
|
||||
public class GetUserImage : ImageRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class DeleteUserImage
|
||||
/// </summary>
|
||||
[Route("/Users/{Id}/Images/{Type}", "DELETE")]
|
||||
[Route("/Users/{Id}/Images/{Type}/{Index}", "DELETE")]
|
||||
public class DeleteUserImage : DeleteImageRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class ImageService
|
||||
/// </summary>
|
||||
[Export(typeof(IRestfulService))]
|
||||
public class ImageService : BaseRestService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetItemImage request)
|
||||
{
|
||||
var item = DtoBuilder.GetItemByClientId(request.Id);
|
||||
|
||||
return GetImage(request, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetUserImage request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var item = kernel.Users.First(i => i.Id == request.Id);
|
||||
|
||||
return GetImage(request, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetYearImage request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var item = kernel.LibraryManager.GetYear(request.Year).Result;
|
||||
|
||||
return GetImage(request, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetStudioImage request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var item = kernel.LibraryManager.GetStudio(request.Name).Result;
|
||||
|
||||
return GetImage(request, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetPersonImage request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var item = kernel.LibraryManager.GetPerson(request.Name).Result;
|
||||
|
||||
return GetImage(request, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetGenreImage request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var item = kernel.LibraryManager.GetGenre(request.Name).Result;
|
||||
|
||||
return GetImage(request, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public void Delete(DeleteUserImage request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var item = kernel.Users.First(i => i.Id == request.Id);
|
||||
|
||||
var task = item.DeleteImage(request.Type);
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the image.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
/// <exception cref="ResourceNotFoundException"></exception>
|
||||
private object GetImage(ImageRequest request, BaseItem item)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var index = request.Index ?? 0;
|
||||
|
||||
var imagePath = GetImagePath(kernel, request, item);
|
||||
|
||||
if (string.IsNullOrEmpty(imagePath))
|
||||
{
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
// See if we can avoid a file system lookup by looking for the file in ResolveArgs
|
||||
var originalFileImageDateModified = kernel.ImageManager.GetImageDateModified(item, request.Type, index);
|
||||
|
||||
var supportedImageEnhancers = kernel.ImageEnhancers.Where(i => i.Supports(item, request.Type)).ToList();
|
||||
|
||||
// If the file does not exist GetLastWriteTimeUtc will return jan 1, 1601 as opposed to throwing an exception
|
||||
// http://msdn.microsoft.com/en-us/library/system.io.file.getlastwritetimeutc.aspx
|
||||
if (originalFileImageDateModified.Year == 1601 && !File.Exists(imagePath))
|
||||
{
|
||||
throw new ResourceNotFoundException(string.Format("File not found: {0}", imagePath));
|
||||
}
|
||||
|
||||
var contentType = MimeTypes.GetMimeType(imagePath);
|
||||
var dateLastModified = (supportedImageEnhancers.Select(e => e.LastConfigurationChange(item, request.Type)).Concat(new[] { originalFileImageDateModified })).Max();
|
||||
|
||||
var cacheGuid = kernel.ImageManager.GetImageCacheTag(imagePath, originalFileImageDateModified, supportedImageEnhancers, item, request.Type);
|
||||
|
||||
TimeSpan? cacheDuration = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(request.Tag) && cacheGuid == new Guid(request.Tag))
|
||||
{
|
||||
cacheDuration = TimeSpan.FromDays(365);
|
||||
}
|
||||
|
||||
return ToCachedResult(cacheGuid, dateLastModified, cacheDuration, () => new ImageWriter
|
||||
{
|
||||
Item = item,
|
||||
Request = request,
|
||||
CropWhiteSpace = request.Type == ImageType.Logo || request.Type == ImageType.Art,
|
||||
OriginalImageDateModified = originalFileImageDateModified,
|
||||
ContentType = contentType
|
||||
|
||||
}, contentType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the image path.
|
||||
/// </summary>
|
||||
/// <param name="kernel">The kernel.</param>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
private string GetImagePath(Kernel kernel, ImageRequest request, BaseItem item)
|
||||
{
|
||||
var index = request.Index ?? 0;
|
||||
|
||||
return kernel.ImageManager.GetImagePath(item, request.Type, index);
|
||||
}
|
||||
}
|
||||
}
|
80
MediaBrowser.Api/Images/ImageWriter.cs
Normal file
80
MediaBrowser.Api/Images/ImageWriter.cs
Normal file
@ -0,0 +1,80 @@
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using ServiceStack.Service;
|
||||
using ServiceStack.ServiceHost;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.Images
|
||||
{
|
||||
/// <summary>
|
||||
/// Class ImageWriter
|
||||
/// </summary>
|
||||
public class ImageWriter : IStreamWriter, IHasOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the request.
|
||||
/// </summary>
|
||||
/// <value>The request.</value>
|
||||
public ImageRequest Request { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the item.
|
||||
/// </summary>
|
||||
/// <value>The item.</value>
|
||||
public BaseItem Item { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether [crop white space].
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if [crop white space]; otherwise, <c>false</c>.</value>
|
||||
public bool CropWhiteSpace { get; set; }
|
||||
/// <summary>
|
||||
/// The original image date modified
|
||||
/// </summary>
|
||||
public DateTime OriginalImageDateModified;
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the content.
|
||||
/// </summary>
|
||||
/// <value>The type of the content.</value>
|
||||
public string ContentType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Writes to.
|
||||
/// </summary>
|
||||
/// <param name="responseStream">The response stream.</param>
|
||||
public void WriteTo(Stream responseStream)
|
||||
{
|
||||
Options["Content-Type"] = ContentType;
|
||||
|
||||
var task = WriteToAsync(responseStream);
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes to async.
|
||||
/// </summary>
|
||||
/// <param name="responseStream">The response stream.</param>
|
||||
/// <returns>Task.</returns>
|
||||
private Task WriteToAsync(Stream responseStream)
|
||||
{
|
||||
return Kernel.Instance.ImageManager.ProcessImage(Item, Request.Type, Request.Index ?? 0, CropWhiteSpace,
|
||||
OriginalImageDateModified, responseStream, Request.Width, Request.Height, Request.MaxWidth,
|
||||
Request.MaxHeight, Request.Quality);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The _options
|
||||
/// </summary>
|
||||
private readonly IDictionary<string, string> _options = new Dictionary<string, string> { };
|
||||
/// <summary>
|
||||
/// Gets the options.
|
||||
/// </summary>
|
||||
/// <value>The options.</value>
|
||||
public IDictionary<string, string> Options
|
||||
{
|
||||
get { return _options; }
|
||||
}
|
||||
}
|
||||
}
|
143
MediaBrowser.Api/Images/UploadImageHandler.cs
Normal file
143
MediaBrowser.Api/Images/UploadImageHandler.cs
Normal file
@ -0,0 +1,143 @@
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.Images
|
||||
{
|
||||
/// <summary>
|
||||
/// Class UploadImageHandler
|
||||
/// </summary>
|
||||
[Export(typeof(IHttpServerHandler))]
|
||||
class UploadImageHandler : BaseActionHandler<Kernel>
|
||||
{
|
||||
/// <summary>
|
||||
/// The _source entity
|
||||
/// </summary>
|
||||
private BaseItem _sourceEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the source entity.
|
||||
/// </summary>
|
||||
/// <returns>Task{BaseItem}.</returns>
|
||||
private async Task<BaseItem> GetSourceEntity()
|
||||
{
|
||||
if (_sourceEntity == null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(QueryString["personname"]))
|
||||
{
|
||||
_sourceEntity =
|
||||
await Kernel.LibraryManager.GetPerson(QueryString["personname"]).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
else if (!string.IsNullOrEmpty(QueryString["genre"]))
|
||||
{
|
||||
_sourceEntity =
|
||||
await Kernel.LibraryManager.GetGenre(QueryString["genre"]).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
else if (!string.IsNullOrEmpty(QueryString["year"]))
|
||||
{
|
||||
_sourceEntity =
|
||||
await
|
||||
Kernel.LibraryManager.GetYear(int.Parse(QueryString["year"])).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
else if (!string.IsNullOrEmpty(QueryString["studio"]))
|
||||
{
|
||||
_sourceEntity =
|
||||
await Kernel.LibraryManager.GetStudio(QueryString["studio"]).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
else if (!string.IsNullOrEmpty(QueryString["userid"]))
|
||||
{
|
||||
_sourceEntity = ApiService.GetUserById(QueryString["userid"]);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
_sourceEntity = DtoBuilder.GetItemByClientId(QueryString["id"]);
|
||||
}
|
||||
}
|
||||
|
||||
return _sourceEntity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the image.
|
||||
/// </summary>
|
||||
/// <value>The type of the image.</value>
|
||||
private ImageType ImageType
|
||||
{
|
||||
get
|
||||
{
|
||||
var imageType = QueryString["type"];
|
||||
|
||||
return (ImageType)Enum.Parse(typeof(ImageType), imageType, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the action.
|
||||
/// </summary>
|
||||
/// <returns>Task.</returns>
|
||||
protected override async Task ExecuteAction()
|
||||
{
|
||||
var entity = await GetSourceEntity().ConfigureAwait(false);
|
||||
|
||||
using (var reader = new StreamReader(HttpListenerContext.Request.InputStream))
|
||||
{
|
||||
var text = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||
|
||||
var bytes = Convert.FromBase64String(text);
|
||||
|
||||
string filename;
|
||||
|
||||
switch (ImageType)
|
||||
{
|
||||
case ImageType.Art:
|
||||
filename = "clearart";
|
||||
break;
|
||||
case ImageType.Primary:
|
||||
filename = "folder";
|
||||
break;
|
||||
default:
|
||||
filename = ImageType.ToString().ToLower();
|
||||
break;
|
||||
}
|
||||
|
||||
// Use the client filename to determine the original extension
|
||||
var clientFileName = QueryString["filename"];
|
||||
|
||||
var oldImagePath = entity.GetImage(ImageType);
|
||||
|
||||
var imagePath = Path.Combine(entity.MetaLocation, filename + Path.GetExtension(clientFileName));
|
||||
|
||||
// Save to file system
|
||||
using (var fs = new FileStream(imagePath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, true))
|
||||
{
|
||||
await fs.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// Set the image
|
||||
entity.SetImage(ImageType, imagePath);
|
||||
|
||||
// If the new and old paths are different, delete the old one
|
||||
if (!string.IsNullOrEmpty(oldImagePath) && !oldImagePath.Equals(imagePath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
File.Delete(oldImagePath);
|
||||
}
|
||||
|
||||
// Directory watchers should repeat this, but do a quick refresh first
|
||||
await entity.RefreshMetadata(CancellationToken.None, forceSave: true, allowSlowProviders: false).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
250
MediaBrowser.Api/LibraryService.cs
Normal file
250
MediaBrowser.Api/LibraryService.cs
Normal file
@ -0,0 +1,250 @@
|
||||
using MediaBrowser.Common.Mef;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using ServiceStack.ServiceHost;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetPhyscialPaths
|
||||
/// </summary>
|
||||
[Route("/Library/PhysicalPaths", "GET")]
|
||||
public class GetPhyscialPaths : IReturn<List<string>>
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetItemTypes
|
||||
/// </summary>
|
||||
[Route("/Library/ItemTypes", "GET")]
|
||||
public class GetItemTypes : IReturn<List<string>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance has internet provider.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance has internet provider; otherwise, <c>false</c>.</value>
|
||||
public bool HasInternetProvider { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetPerson
|
||||
/// </summary>
|
||||
[Route("/Library/Persons/{Name}", "GET")]
|
||||
public class GetPerson : IReturn<DtoBaseItem>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetStudio
|
||||
/// </summary>
|
||||
[Route("/Library/Studios/{Name}", "GET")]
|
||||
public class GetStudio : IReturn<DtoBaseItem>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetGenre
|
||||
/// </summary>
|
||||
[Route("/Library/Genres/{Name}", "GET")]
|
||||
public class GetGenre : IReturn<DtoBaseItem>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetYear
|
||||
/// </summary>
|
||||
[Route("/Library/Years/{Year}", "GET")]
|
||||
public class GetYear : IReturn<DtoBaseItem>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the year.
|
||||
/// </summary>
|
||||
/// <value>The year.</value>
|
||||
public int Year { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetDefaultVirtualFolders
|
||||
/// </summary>
|
||||
[Route("/Library/DefaultVirtualFolders", "GET")]
|
||||
public class GetDefaultVirtualFolders : IReturn<List<VirtualFolderInfo>>
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class LibraryService
|
||||
/// </summary>
|
||||
[Export(typeof(IRestfulService))]
|
||||
public class LibraryService : BaseRestService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetDefaultVirtualFolders request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var result = kernel.LibraryManager.GetDefaultVirtualFolders().ToList();
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetPerson request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var item = kernel.LibraryManager.GetPerson(request.Name).Result;
|
||||
|
||||
// Get everything
|
||||
var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true));
|
||||
|
||||
var result = DtoBuilder.GetDtoBaseItem(item, fields.ToList()).Result;
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetGenre request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var item = kernel.LibraryManager.GetGenre(request.Name).Result;
|
||||
|
||||
// Get everything
|
||||
var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true));
|
||||
|
||||
var result = DtoBuilder.GetDtoBaseItem(item, fields.ToList()).Result;
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetStudio request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var item = kernel.LibraryManager.GetStudio(request.Name).Result;
|
||||
|
||||
// Get everything
|
||||
var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true));
|
||||
|
||||
var result = DtoBuilder.GetDtoBaseItem(item, fields.ToList()).Result;
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetYear request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var item = kernel.LibraryManager.GetYear(request.Year).Result;
|
||||
|
||||
// Get everything
|
||||
var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true));
|
||||
|
||||
var result = DtoBuilder.GetDtoBaseItem(item, fields.ToList()).Result;
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetPhyscialPaths request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var result = kernel.RootFolder.Children.SelectMany(c => c.ResolveArgs.PhysicalLocations).ToList();
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetItemTypes request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var allTypes = kernel.Assemblies.SelectMany(MefUtils.GetTypes).Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(BaseItem)));
|
||||
|
||||
if (request.HasInternetProvider)
|
||||
{
|
||||
allTypes = allTypes.Where(t =>
|
||||
{
|
||||
if (t == typeof(UserRootFolder) || t == typeof(AggregateFolder) || t == typeof(Folder) || t == typeof(IndexFolder) || t == typeof(CollectionFolder) || t == typeof(Year))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (t == typeof(User))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// For now it seems internet providers generally only deal with video subclasses
|
||||
if (t == typeof(Video))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (t.IsSubclassOf(typeof(BasePluginFolder)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
return allTypes.Select(t => t.Name).OrderBy(s => s).ToList();
|
||||
}
|
||||
}
|
||||
}
|
112
MediaBrowser.Api/LocalizationService.cs
Normal file
112
MediaBrowser.Api/LocalizationService.cs
Normal file
@ -0,0 +1,112 @@
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Localization;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
using MoreLinq;
|
||||
using ServiceStack.ServiceHost;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetCultures
|
||||
/// </summary>
|
||||
[Route("/Localization/Cultures", "GET")]
|
||||
public class GetCultures : IReturn<List<CultureDto>>
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetCountries
|
||||
/// </summary>
|
||||
[Route("/Localization/Countries", "GET")]
|
||||
public class GetCountries : IReturn<List<CountryInfo>>
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class ParentalRatings
|
||||
/// </summary>
|
||||
[Route("/Localization/ParentalRatings", "GET")]
|
||||
public class GetParentalRatings : IReturn<List<ParentalRating>>
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class CulturesService
|
||||
/// </summary>
|
||||
[Export(typeof(IRestfulService))]
|
||||
public class LocalizationService : BaseRestService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetParentalRatings request)
|
||||
{
|
||||
var ratings =
|
||||
Ratings.RatingsDict.Select(k => new ParentalRating { Name = k.Key, Value = k.Value });
|
||||
|
||||
var result = ratings.OrderBy(p => p.Value).Where(p => p.Value > 0).ToList();
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetCountries request)
|
||||
{
|
||||
var result = CultureInfo.GetCultures(CultureTypes.SpecificCultures)
|
||||
|
||||
.Select(c => new RegionInfo(c.LCID))
|
||||
.OrderBy(c => c.DisplayName)
|
||||
|
||||
// Try to eliminate dupes
|
||||
.DistinctBy(c => c.TwoLetterISORegionName)
|
||||
|
||||
.Select(c => new CountryInfo
|
||||
{
|
||||
Name = c.Name,
|
||||
DisplayName = c.DisplayName,
|
||||
TwoLetterISORegionName = c.TwoLetterISORegionName,
|
||||
ThreeLetterISORegionName = c.ThreeLetterISORegionName
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetCultures request)
|
||||
{
|
||||
var result = CultureInfo.GetCultures(CultureTypes.AllCultures)
|
||||
.OrderBy(c => c.DisplayName)
|
||||
|
||||
// Try to eliminate dupes
|
||||
.DistinctBy(c => c.TwoLetterISOLanguageName + c.ThreeLetterISOLanguageName)
|
||||
|
||||
.Select(c => new CultureDto
|
||||
{
|
||||
Name = c.Name,
|
||||
DisplayName = c.DisplayName,
|
||||
ThreeLetterISOLanguageName = c.ThreeLetterISOLanguageName,
|
||||
TwoLetterISOLanguageName = c.TwoLetterISOLanguageName
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,117 +1,161 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{4FD51AC5-2C16-4308-A993-C3A84F3B4582}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MediaBrowser.Api</RootNamespace>
|
||||
<AssemblyName>MediaBrowser.Api</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RunPostBuildEvent>Always</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Reactive.Core, Version=2.0.20823.0, Culture=neutral, PublicKeyToken=f300afd708cefcd3, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Rx-Core.2.0.20823\lib\Net45\System.Reactive.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Reactive.Interfaces, Version=2.0.20823.0, Culture=neutral, PublicKeyToken=f300afd708cefcd3, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Rx-Interfaces.2.0.20823\lib\Net45\System.Reactive.Interfaces.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Reactive.Linq, Version=2.0.20823.0, Culture=neutral, PublicKeyToken=f300afd708cefcd3, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Rx-Linq.2.0.20823\lib\Net45\System.Reactive.Linq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ApiService.cs" />
|
||||
<Compile Include="HttpHandlers\AudioHandler.cs" />
|
||||
<Compile Include="HttpHandlers\BaseMediaHandler.cs" />
|
||||
<Compile Include="HttpHandlers\FavoriteStatusHandler.cs" />
|
||||
<Compile Include="HttpHandlers\MovieSpecialFeaturesHandler.cs" />
|
||||
<Compile Include="HttpHandlers\PlayedStatusHandler.cs" />
|
||||
<Compile Include="HttpHandlers\UserHandler.cs" />
|
||||
<Compile Include="HttpHandlers\GenreHandler.cs" />
|
||||
<Compile Include="HttpHandlers\GenresHandler.cs" />
|
||||
<Compile Include="HttpHandlers\ImageHandler.cs" />
|
||||
<Compile Include="HttpHandlers\ItemHandler.cs" />
|
||||
<Compile Include="HttpHandlers\ItemListHandler.cs" />
|
||||
<Compile Include="HttpHandlers\PersonHandler.cs" />
|
||||
<Compile Include="HttpHandlers\PluginAssemblyHandler.cs" />
|
||||
<Compile Include="HttpHandlers\PluginConfigurationHandler.cs" />
|
||||
<Compile Include="HttpHandlers\PluginsHandler.cs" />
|
||||
<Compile Include="HttpHandlers\ServerConfigurationHandler.cs" />
|
||||
<Compile Include="HttpHandlers\StudioHandler.cs" />
|
||||
<Compile Include="HttpHandlers\StudiosHandler.cs" />
|
||||
<Compile Include="HttpHandlers\UserAuthenticationHandler.cs" />
|
||||
<Compile Include="HttpHandlers\UserItemRatingHandler.cs" />
|
||||
<Compile Include="HttpHandlers\UsersHandler.cs" />
|
||||
<Compile Include="HttpHandlers\VideoHandler.cs" />
|
||||
<Compile Include="HttpHandlers\WeatherHandler.cs" />
|
||||
<Compile Include="HttpHandlers\YearHandler.cs" />
|
||||
<Compile Include="HttpHandlers\YearsHandler.cs" />
|
||||
<Compile Include="Plugin.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
|
||||
<Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
|
||||
<Name>MediaBrowser.Common</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj">
|
||||
<Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
|
||||
<Name>MediaBrowser.Controller</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
|
||||
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
|
||||
<Name>MediaBrowser.Model</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>xcopy "$(TargetPath)" "$(SolutionDir)\ProgramData-Server\Plugins\" /y</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{4FD51AC5-2C16-4308-A993-C3A84F3B4582}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MediaBrowser.Api</RootNamespace>
|
||||
<AssemblyName>MediaBrowser.Api</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||
<RestorePackages>true</RestorePackages>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RunPostBuildEvent>Always</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="MoreLinq">
|
||||
<HintPath>..\packages\morelinq.1.0.15631-beta\lib\net35\MoreLinq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="protobuf-net, Version=2.0.0.621, Culture=neutral, PublicKeyToken=257b51d87d2e4d67, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\protobuf-net.2.0.0.621\lib\net40\protobuf-net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ServiceStack">
|
||||
<HintPath>..\packages\ServiceStack.3.9.37\lib\net35\ServiceStack.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ServiceStack.Common">
|
||||
<HintPath>..\packages\ServiceStack.Common.3.9.37\lib\net35\ServiceStack.Common.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ServiceStack.Interfaces">
|
||||
<HintPath>..\packages\ServiceStack.Common.3.9.37\lib\net35\ServiceStack.Interfaces.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ServiceStack.OrmLite">
|
||||
<HintPath>..\packages\ServiceStack.OrmLite.SqlServer.3.9.37\lib\ServiceStack.OrmLite.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ServiceStack.OrmLite.SqlServer">
|
||||
<HintPath>..\packages\ServiceStack.OrmLite.SqlServer.3.9.37\lib\ServiceStack.OrmLite.SqlServer.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ServiceStack.Redis">
|
||||
<HintPath>..\packages\ServiceStack.Redis.3.9.37\lib\net35\ServiceStack.Redis.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ServiceStack.ServiceInterface">
|
||||
<HintPath>..\packages\ServiceStack.3.9.37\lib\net35\ServiceStack.ServiceInterface.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ServiceStack.Text">
|
||||
<HintPath>..\packages\ServiceStack.Text.3.9.37\lib\net35\ServiceStack.Text.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Deployment" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Reactive.Core, Version=2.0.20823.0, Culture=neutral, PublicKeyToken=f300afd708cefcd3, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Rx-Core.2.0.21114\lib\Net45\System.Reactive.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Reactive.Interfaces, Version=2.0.20823.0, Culture=neutral, PublicKeyToken=f300afd708cefcd3, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Rx-Interfaces.2.0.21114\lib\Net45\System.Reactive.Interfaces.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Reactive.Linq, Version=2.0.20823.0, Culture=neutral, PublicKeyToken=f300afd708cefcd3, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\Rx-Linq.2.0.21114\lib\Net45\System.Reactive.Linq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ApiService.cs" />
|
||||
<Compile Include="EnvironmentService.cs" />
|
||||
<Compile Include="HttpHandlers\PlaybackCheckInHandler.cs" />
|
||||
<Compile Include="Images\ImageRequest.cs" />
|
||||
<Compile Include="Images\ImageService.cs" />
|
||||
<Compile Include="Images\ImageWriter.cs" />
|
||||
<Compile Include="Images\UploadImageHandler.cs" />
|
||||
<Compile Include="LibraryService.cs" />
|
||||
<Compile Include="LocalizationService.cs" />
|
||||
<Compile Include="PackageService.cs" />
|
||||
<Compile Include="PluginService.cs" />
|
||||
<Compile Include="SystemService.cs" />
|
||||
<Compile Include="Streaming\AudioHandler.cs" />
|
||||
<Compile Include="Streaming\BaseHlsPlaylistHandler.cs" />
|
||||
<Compile Include="Streaming\BaseProgressiveStreamingHandler.cs" />
|
||||
<Compile Include="Streaming\BaseStreamingHandler.cs" />
|
||||
<Compile Include="UserLibrary\BaseItemsByNameService.cs" />
|
||||
<Compile Include="UserLibrary\GenresService.cs" />
|
||||
<Compile Include="UserLibrary\ItemsService.cs" />
|
||||
<Compile Include="UserLibrary\PersonsService.cs" />
|
||||
<Compile Include="UserLibrary\StudiosService.cs" />
|
||||
<Compile Include="UserLibrary\UserLibraryService.cs" />
|
||||
<Compile Include="UserLibrary\YearsService.cs" />
|
||||
<Compile Include="UserService.cs" />
|
||||
<Compile Include="Streaming\HlsAudioPlaylistHandler.cs" />
|
||||
<Compile Include="Streaming\HlsSegmentHandler.cs" />
|
||||
<Compile Include="Streaming\HlsVideoPlaylistHandler.cs" />
|
||||
<Compile Include="HttpHandlers\UpdateMediaLibraryHandler.cs" />
|
||||
<Compile Include="Streaming\VideoHandler.cs" />
|
||||
<Compile Include="Plugin.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="WeatherService.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
|
||||
<Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
|
||||
<Name>MediaBrowser.Common</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj">
|
||||
<Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
|
||||
<Name>MediaBrowser.Controller</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
|
||||
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
|
||||
<Name>MediaBrowser.Model</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="options.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>xcopy "$(TargetPath)" "$(SolutionDir)\MediaBrowser.ServerApplication\" /y</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
201
MediaBrowser.Api/PackageService.cs
Normal file
201
MediaBrowser.Api/PackageService.cs
Normal file
@ -0,0 +1,201 @@
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Updates;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using MediaBrowser.Model.Updates;
|
||||
using ServiceStack.ServiceHost;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetPackage
|
||||
/// </summary>
|
||||
[Route("/Packages/{Name}", "GET")]
|
||||
public class GetPackage : IReturn<PackageInfo>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetPackages
|
||||
/// </summary>
|
||||
[Route("/Packages", "GET")]
|
||||
public class GetPackages : IReturn<List<PackageInfo>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public PackageType? PackageType { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetPackageVersionUpdates
|
||||
/// </summary>
|
||||
[Route("/Packages/Updates", "GET")]
|
||||
public class GetPackageVersionUpdates : IReturn<List<PackageVersionInfo>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public PackageType PackageType { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class InstallPackage
|
||||
/// </summary>
|
||||
[Route("/Packages/Installed/{Name}", "POST")]
|
||||
public class InstallPackage : IReturnVoid
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the version.
|
||||
/// </summary>
|
||||
/// <value>The version.</value>
|
||||
public string Version { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the update class.
|
||||
/// </summary>
|
||||
/// <value>The update class.</value>
|
||||
public PackageVersionClass UpdateClass { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class CancelPackageInstallation
|
||||
/// </summary>
|
||||
[Route("/Packages/Installing/{Id}", "DELETE")]
|
||||
public class CancelPackageInstallation : IReturnVoid
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class PackageService
|
||||
/// </summary>
|
||||
[Export(typeof(IRestfulService))]
|
||||
public class PackageService : BaseRestService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
/// <exception cref="System.ArgumentException">Unsupported PackageType</exception>
|
||||
public object Get(GetPackageVersionUpdates request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var result = new List<PackageVersionInfo>();
|
||||
|
||||
if (request.PackageType == PackageType.UserInstalled || request.PackageType == PackageType.All)
|
||||
{
|
||||
result.AddRange(kernel.InstallationManager.GetAvailablePluginUpdates(false, CancellationToken.None).Result.ToList());
|
||||
}
|
||||
|
||||
else if (request.PackageType == PackageType.System || request.PackageType == PackageType.All)
|
||||
{
|
||||
var updateCheckResult = new ApplicationUpdateCheck().CheckForApplicationUpdate(CancellationToken.None, new Progress<TaskProgress> { }).Result;
|
||||
|
||||
if (updateCheckResult.UpdateAvailable)
|
||||
{
|
||||
result.Add(new PackageVersionInfo
|
||||
{
|
||||
versionStr = updateCheckResult.AvailableVersion.ToString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetPackage request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var packages = kernel.InstallationManager.GetAvailablePackages(CancellationToken.None, applicationVersion: kernel.ApplicationVersion).Result;
|
||||
|
||||
var result = packages.FirstOrDefault(p => p.name.Equals(request.Name, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetPackages request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var packages = kernel.InstallationManager.GetAvailablePackages(CancellationToken.None, request.PackageType, kernel.ApplicationVersion).Result;
|
||||
|
||||
return ToOptimizedResult(packages.ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <exception cref="ResourceNotFoundException"></exception>
|
||||
public void Post(InstallPackage request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var package = string.IsNullOrEmpty(request.Version) ?
|
||||
kernel.InstallationManager.GetLatestCompatibleVersion(request.Name, request.UpdateClass).Result :
|
||||
kernel.InstallationManager.GetPackage(request.Name, request.UpdateClass, Version.Parse(request.Version)).Result;
|
||||
|
||||
if (package == null)
|
||||
{
|
||||
throw new ResourceNotFoundException(string.Format("Package not found: {0}", request.Name));
|
||||
}
|
||||
|
||||
Task.Run(() => kernel.InstallationManager.InstallPackage(package, new Progress<double> { }, CancellationToken.None));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public void Delete(CancelPackageInstallation request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var info = kernel.InstallationManager.CurrentInstallations.FirstOrDefault(i => i.Item1.Id == request.Id);
|
||||
|
||||
if (info != null)
|
||||
{
|
||||
info.Item2.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,14 +1,329 @@
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using System.ComponentModel.Composition;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
{
|
||||
[Export(typeof(BasePlugin))]
|
||||
public class Plugin : BasePlugin
|
||||
{
|
||||
public override string Name
|
||||
{
|
||||
get { return "Media Browser API"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Model.Plugins;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// Class Plugin
|
||||
/// </summary>
|
||||
[Export(typeof(IPlugin))]
|
||||
public class Plugin : BasePlugin<BasePluginConfiguration>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the plugin
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public override string Name
|
||||
{
|
||||
get { return "Web Api"; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is a core plugin.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is a core plugin; otherwise, <c>false</c>.</value>
|
||||
public override bool IsCorePlugin
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance.
|
||||
/// </summary>
|
||||
/// <value>The instance.</value>
|
||||
public static Plugin Instance { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Plugin" /> class.
|
||||
/// </summary>
|
||||
public Plugin()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
protected override void DisposeOnServer(bool dispose)
|
||||
{
|
||||
if (dispose)
|
||||
{
|
||||
var jobCount = ActiveTranscodingJobs.Count;
|
||||
|
||||
Parallel.ForEach(ActiveTranscodingJobs, OnTranscodeKillTimerStopped);
|
||||
|
||||
// Try to allow for some time to kill the ffmpeg processes and delete the partial stream files
|
||||
if (jobCount > 0)
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
base.DisposeOnServer(dispose);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The active transcoding jobs
|
||||
/// </summary>
|
||||
private readonly List<TranscodingJob> ActiveTranscodingJobs = new List<TranscodingJob>();
|
||||
|
||||
/// <summary>
|
||||
/// Called when [transcode beginning].
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="process">The process.</param>
|
||||
public void OnTranscodeBeginning(string path, TranscodingJobType type, Process process)
|
||||
{
|
||||
lock (ActiveTranscodingJobs)
|
||||
{
|
||||
ActiveTranscodingJobs.Add(new TranscodingJob
|
||||
{
|
||||
Type = type,
|
||||
Path = path,
|
||||
Process = process,
|
||||
ActiveRequestCount = 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when [transcode failed to start].
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="type">The type.</param>
|
||||
public void OnTranscodeFailedToStart(string path, TranscodingJobType type)
|
||||
{
|
||||
lock (ActiveTranscodingJobs)
|
||||
{
|
||||
var job = ActiveTranscodingJobs.First(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
ActiveTranscodingJobs.Remove(job);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether [has active transcoding job] [the specified path].
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <returns><c>true</c> if [has active transcoding job] [the specified path]; otherwise, <c>false</c>.</returns>
|
||||
public bool HasActiveTranscodingJob(string path, TranscodingJobType type)
|
||||
{
|
||||
lock (ActiveTranscodingJobs)
|
||||
{
|
||||
return ActiveTranscodingJobs.Any(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when [transcode begin request].
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="type">The type.</param>
|
||||
public void OnTranscodeBeginRequest(string path, TranscodingJobType type)
|
||||
{
|
||||
lock (ActiveTranscodingJobs)
|
||||
{
|
||||
var job = ActiveTranscodingJobs.FirstOrDefault(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (job == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
job.ActiveRequestCount++;
|
||||
|
||||
if (job.KillTimer != null)
|
||||
{
|
||||
job.KillTimer.Dispose();
|
||||
job.KillTimer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when [transcode end request].
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="type">The type.</param>
|
||||
public void OnTranscodeEndRequest(string path, TranscodingJobType type)
|
||||
{
|
||||
lock (ActiveTranscodingJobs)
|
||||
{
|
||||
var job = ActiveTranscodingJobs.FirstOrDefault(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (job == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
job.ActiveRequestCount--;
|
||||
|
||||
if (job.ActiveRequestCount == 0)
|
||||
{
|
||||
var timerDuration = type == TranscodingJobType.Progressive ? 1000 : 30000;
|
||||
|
||||
if (job.KillTimer == null)
|
||||
{
|
||||
job.KillTimer = new Timer(OnTranscodeKillTimerStopped, job, timerDuration, Timeout.Infinite);
|
||||
}
|
||||
else
|
||||
{
|
||||
job.KillTimer.Change(timerDuration, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when [transcoding finished].
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="type">The type.</param>
|
||||
public void OnTranscodingFinished(string path, TranscodingJobType type)
|
||||
{
|
||||
lock (ActiveTranscodingJobs)
|
||||
{
|
||||
var job = ActiveTranscodingJobs.FirstOrDefault(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (job == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ActiveTranscodingJobs.Remove(job);
|
||||
|
||||
if (job.KillTimer != null)
|
||||
{
|
||||
job.KillTimer.Dispose();
|
||||
job.KillTimer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when [transcode kill timer stopped].
|
||||
/// </summary>
|
||||
/// <param name="state">The state.</param>
|
||||
private void OnTranscodeKillTimerStopped(object state)
|
||||
{
|
||||
var job = (TranscodingJob)state;
|
||||
|
||||
lock (ActiveTranscodingJobs)
|
||||
{
|
||||
ActiveTranscodingJobs.Remove(job);
|
||||
|
||||
if (job.KillTimer != null)
|
||||
{
|
||||
job.KillTimer.Dispose();
|
||||
job.KillTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
var process = job.Process;
|
||||
|
||||
var hasExited = true;
|
||||
|
||||
try
|
||||
{
|
||||
hasExited = process.HasExited;
|
||||
}
|
||||
catch (Win32Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error determining if ffmpeg process has exited for {0}", ex, job.Path);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
Logger.ErrorException("Error determining if ffmpeg process has exited for {0}", ex, job.Path);
|
||||
}
|
||||
catch (NotSupportedException ex)
|
||||
{
|
||||
Logger.ErrorException("Error determining if ffmpeg process has exited for {0}", ex, job.Path);
|
||||
}
|
||||
|
||||
if (hasExited)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Logger.Info("Killing ffmpeg process for {0}", job.Path);
|
||||
|
||||
process.Kill();
|
||||
}
|
||||
catch (Win32Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error killing transcoding job for {0}", ex, job.Path);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
Logger.ErrorException("Error killing transcoding job for {0}", ex, job.Path);
|
||||
}
|
||||
catch (NotSupportedException ex)
|
||||
{
|
||||
Logger.ErrorException("Error killing transcoding job for {0}", ex, job.Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class TranscodingJob
|
||||
/// </summary>
|
||||
public class TranscodingJob
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the path.
|
||||
/// </summary>
|
||||
/// <value>The path.</value>
|
||||
public string Path { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the type.
|
||||
/// </summary>
|
||||
/// <value>The type.</value>
|
||||
public TranscodingJobType Type { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the process.
|
||||
/// </summary>
|
||||
/// <value>The process.</value>
|
||||
public Process Process { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the active request count.
|
||||
/// </summary>
|
||||
/// <value>The active request count.</value>
|
||||
public int ActiveRequestCount { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the kill timer.
|
||||
/// </summary>
|
||||
/// <value>The kill timer.</value>
|
||||
public Timer KillTimer { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enum TranscodingJobType
|
||||
/// </summary>
|
||||
public enum TranscodingJobType
|
||||
{
|
||||
/// <summary>
|
||||
/// The progressive
|
||||
/// </summary>
|
||||
Progressive,
|
||||
/// <summary>
|
||||
/// The HLS
|
||||
/// </summary>
|
||||
Hls
|
||||
}
|
||||
}
|
||||
|
241
MediaBrowser.Api/PluginService.cs
Normal file
241
MediaBrowser.Api/PluginService.cs
Normal file
@ -0,0 +1,241 @@
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Serialization;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Plugins;
|
||||
using ServiceStack.ServiceHost;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using ServiceStack.Text.Controller;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// Class Plugins
|
||||
/// </summary>
|
||||
[Route("/Plugins", "GET")]
|
||||
public class GetPlugins : IReturn<List<PluginInfo>>
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetPluginAssembly
|
||||
/// </summary>
|
||||
[Route("/Plugins/{Id}/Assembly", "GET")]
|
||||
public class GetPluginAssembly
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class UninstallPlugin
|
||||
/// </summary>
|
||||
[Route("/Plugins/{Id}", "DELETE")]
|
||||
public class UninstallPlugin : IReturnVoid
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetPluginConfiguration
|
||||
/// </summary>
|
||||
[Route("/Plugins/{Id}/Configuration", "GET")]
|
||||
public class GetPluginConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class UpdatePluginConfiguration
|
||||
/// </summary>
|
||||
[Route("/Plugins/{Id}/Configuration", "POST")]
|
||||
public class UpdatePluginConfiguration : IRequiresRequestStream, IReturnVoid
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The raw Http Request Input Stream
|
||||
/// </summary>
|
||||
/// <value>The request stream.</value>
|
||||
public Stream RequestStream { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetPluginConfigurationFile
|
||||
/// </summary>
|
||||
[Route("/Plugins/{Id}/ConfigurationFile", "GET")]
|
||||
public class GetPluginConfigurationFile
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetPluginSecurityInfo
|
||||
/// </summary>
|
||||
[Route("/Plugins/SecurityInfo", "GET")]
|
||||
[Restrict(VisibleLocalhostOnly = true)]
|
||||
public class GetPluginSecurityInfo : IReturn<PluginSecurityInfo>
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class UpdatePluginSecurityInfo
|
||||
/// </summary>
|
||||
[Route("/Plugins/SecurityInfo", "GET")]
|
||||
public class UpdatePluginSecurityInfo : IReturnVoid, IRequiresRequestStream
|
||||
{
|
||||
/// <summary>
|
||||
/// The raw Http Request Input Stream
|
||||
/// </summary>
|
||||
/// <value>The request stream.</value>
|
||||
public Stream RequestStream { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class PluginsService
|
||||
/// </summary>
|
||||
[Export(typeof(IRestfulService))]
|
||||
public class PluginService : BaseRestService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetPlugins request)
|
||||
{
|
||||
var result = Kernel.Plugins.OrderBy(p => p.Name).Select(p => p.GetPluginInfo()).ToList();
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetPluginAssembly request)
|
||||
{
|
||||
var plugin = Kernel.Plugins.First(p => p.UniqueId == request.Id);
|
||||
|
||||
return ToStaticFileResult(plugin.AssemblyFilePath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetPluginConfiguration request)
|
||||
{
|
||||
var plugin = Kernel.Plugins.First(p => p.UniqueId == request.Id);
|
||||
|
||||
var dateModified = plugin.ConfigurationDateLastModified;
|
||||
|
||||
var cacheKey = (plugin.Version.ToString() + dateModified.Ticks).GetMD5();
|
||||
|
||||
return ToOptimizedResultUsingCache(cacheKey, dateModified, null, () => plugin.Configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetPluginConfigurationFile request)
|
||||
{
|
||||
var plugin = Kernel.Plugins.First(p => p.UniqueId == request.Id);
|
||||
|
||||
return ToStaticFileResult(plugin.ConfigurationFilePath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetPluginSecurityInfo request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var result = new PluginSecurityInfo
|
||||
{
|
||||
IsMBSupporter = kernel.PluginSecurityManager.IsMBSupporter,
|
||||
SupporterKey = kernel.PluginSecurityManager.SupporterKey,
|
||||
LegacyKey = kernel.PluginSecurityManager.LegacyKey
|
||||
};
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public void Post(UpdatePluginSecurityInfo request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var info = JsonSerializer.DeserializeFromStream<PluginSecurityInfo>(request.RequestStream);
|
||||
|
||||
kernel.PluginSecurityManager.SupporterKey = info.SupporterKey;
|
||||
kernel.PluginSecurityManager.LegacyKey = info.LegacyKey;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public void Post(UpdatePluginConfiguration request)
|
||||
{
|
||||
// We need to parse this manually because we told service stack not to with IRequiresRequestStream
|
||||
// https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs
|
||||
var pathInfo = PathInfo.Parse(Request.PathInfo);
|
||||
var id = new Guid(pathInfo.GetArgumentValue<string>(1));
|
||||
|
||||
var plugin = Kernel.Plugins.First(p => p.UniqueId == id);
|
||||
|
||||
var configuration = JsonSerializer.DeserializeFromStream(request.RequestStream, plugin.ConfigurationType) as BasePluginConfiguration;
|
||||
|
||||
plugin.UpdateConfiguration(configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public void Delete(UninstallPlugin request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var plugin = kernel.Plugins.First(p => p.UniqueId == request.Id);
|
||||
|
||||
kernel.InstallationManager.UninstallPlugin(plugin);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +1,34 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("MediaBrowser.Api")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("MediaBrowser.Api")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2012")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("13464b02-f033-48b8-9e1c-d071f8860935")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("MediaBrowser.Api")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("MediaBrowser.Api")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2012")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("13464b02-f033-48b8-9e1c-d071f8860935")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("2.9.*")]
|
||||
|
112
MediaBrowser.Api/Streaming/AudioHandler.cs
Normal file
112
MediaBrowser.Api/Streaming/AudioHandler.cs
Normal file
@ -0,0 +1,112 @@
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Resolvers;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
||||
namespace MediaBrowser.Api.Streaming
|
||||
{
|
||||
/// <summary>
|
||||
/// Providers a progressive streaming audio api
|
||||
/// </summary>
|
||||
[Export(typeof(IHttpServerHandler))]
|
||||
public class AudioHandler : BaseProgressiveStreamingHandler<Audio>
|
||||
{
|
||||
/// <summary>
|
||||
/// Handleses the request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return EntityResolutionHelper.AudioFileExtensions.Any(a => ApiService.IsApiUrlMatch("audio" + a, request));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the audio codec.
|
||||
/// </summary>
|
||||
/// <value>The audio codec.</value>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
protected override AudioCodecs? AudioCodec
|
||||
{
|
||||
get
|
||||
{
|
||||
var ext = OutputFileExtension;
|
||||
|
||||
if (ext.Equals(".aac", StringComparison.OrdinalIgnoreCase) || ext.Equals(".m4a", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return AudioCodecs.Aac;
|
||||
}
|
||||
if (ext.Equals(".mp3", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return AudioCodecs.Mp3;
|
||||
}
|
||||
if (ext.Equals(".wma", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return AudioCodecs.Wma;
|
||||
}
|
||||
if (ext.Equals(".oga", StringComparison.OrdinalIgnoreCase) || ext.Equals(".ogg", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return AudioCodecs.Vorbis;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates arguments to pass to ffmpeg
|
||||
/// </summary>
|
||||
/// <param name="outputPath">The output path.</param>
|
||||
/// <param name="isoMount">The iso mount.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
/// <exception cref="System.InvalidOperationException">Only aac and mp3 audio codecs are supported.</exception>
|
||||
/// <exception cref="InvalidOperationException">Only aac and mp3 audio codecs are supported.</exception>
|
||||
/// <exception cref="ArgumentException">Only aac and mp3 audio codecs are supported.</exception>
|
||||
protected override string GetCommandLineArguments(string outputPath, IIsoMount isoMount)
|
||||
{
|
||||
var audioTranscodeParams = new List<string>();
|
||||
|
||||
var outputFormat = AudioCodec;
|
||||
|
||||
if (outputFormat != AudioCodecs.Aac && outputFormat != AudioCodecs.Mp3)
|
||||
{
|
||||
throw new InvalidOperationException("Only aac and mp3 audio codecs are supported.");
|
||||
}
|
||||
|
||||
if (AudioBitRate.HasValue)
|
||||
{
|
||||
audioTranscodeParams.Add("-ab " + AudioBitRate.Value);
|
||||
}
|
||||
|
||||
var channels = GetNumAudioChannelsParam();
|
||||
|
||||
if (channels.HasValue)
|
||||
{
|
||||
audioTranscodeParams.Add("-ac " + channels.Value);
|
||||
}
|
||||
|
||||
var sampleRate = GetSampleRateParam();
|
||||
|
||||
if (sampleRate.HasValue)
|
||||
{
|
||||
audioTranscodeParams.Add("-ar " + sampleRate.Value);
|
||||
}
|
||||
|
||||
const string vn = " -vn";
|
||||
|
||||
return string.Format("{0} -i {1}{2} -threads 0{5} {3} -id3v2_version 3 -write_id3v1 1 \"{4}\"",
|
||||
FastSeekCommandLineParameter,
|
||||
GetInputArgument(isoMount),
|
||||
SlowSeekCommandLineParameter,
|
||||
string.Join(" ", audioTranscodeParams.ToArray()),
|
||||
outputPath,
|
||||
vn).Trim();
|
||||
}
|
||||
}
|
||||
}
|
221
MediaBrowser.Api/Streaming/BaseHlsPlaylistHandler.cs
Normal file
221
MediaBrowser.Api/Streaming/BaseHlsPlaylistHandler.cs
Normal file
@ -0,0 +1,221 @@
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.Streaming
|
||||
{
|
||||
/// <summary>
|
||||
/// Class BaseHlsPlaylistHandler
|
||||
/// </summary>
|
||||
/// <typeparam name="TBaseItemType">The type of the T base item type.</typeparam>
|
||||
public abstract class BaseHlsPlaylistHandler<TBaseItemType> : BaseStreamingHandler<TBaseItemType>
|
||||
where TBaseItemType : BaseItem, IHasMediaStreams, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the audio arguments.
|
||||
/// </summary>
|
||||
/// <returns>System.String.</returns>
|
||||
protected abstract string GetAudioArguments();
|
||||
/// <summary>
|
||||
/// Gets the video arguments.
|
||||
/// </summary>
|
||||
/// <returns>System.String.</returns>
|
||||
protected abstract string GetVideoArguments();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the transcoding job.
|
||||
/// </summary>
|
||||
/// <value>The type of the transcoding job.</value>
|
||||
protected override TranscodingJobType TranscodingJobType
|
||||
{
|
||||
get { return TranscodingJobType.Hls; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This isn't needed because we're going to override the whole flow using ProcessRequest
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream.</param>
|
||||
/// <param name="responseInfo">The response info.</param>
|
||||
/// <param name="contentLength">Length of the content.</param>
|
||||
/// <returns>Task.</returns>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
protected override Task WriteResponseToOutputStream(Stream stream, ResponseInfo responseInfo, long? contentLength)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the segment file extension.
|
||||
/// </summary>
|
||||
/// <value>The segment file extension.</value>
|
||||
protected abstract string SegmentFileExtension { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Processes the request.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The CTX.</param>
|
||||
/// <returns>Task.</returns>
|
||||
public override async Task ProcessRequest(HttpListenerContext ctx)
|
||||
{
|
||||
HttpListenerContext = ctx;
|
||||
|
||||
var playlist = OutputFilePath;
|
||||
var isPlaylistNewlyCreated = false;
|
||||
|
||||
// If the playlist doesn't already exist, startup ffmpeg
|
||||
if (!File.Exists(playlist))
|
||||
{
|
||||
isPlaylistNewlyCreated = true;
|
||||
await StartFFMpeg(playlist).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
Plugin.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
|
||||
}
|
||||
|
||||
// Get the current playlist text and convert to bytes
|
||||
var playlistText = await GetPlaylistFileText(playlist, isPlaylistNewlyCreated).ConfigureAwait(false);
|
||||
|
||||
var content = Encoding.UTF8.GetBytes(playlistText);
|
||||
|
||||
var stream = new MemoryStream(content);
|
||||
|
||||
try
|
||||
{
|
||||
// Dump the stream off to the static file handler to serve statically
|
||||
await new StaticFileHandler(Kernel) { ContentType = MimeTypes.GetMimeType("playlist.m3u8"), SourceStream = stream }.ProcessRequest(ctx);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Plugin.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current playlist text
|
||||
/// </summary>
|
||||
/// <param name="playlist">The path to the playlist</param>
|
||||
/// <param name="waitForMinimumSegments">Whether or not we should wait until it contains three segments</param>
|
||||
/// <returns>Task{System.String}.</returns>
|
||||
private async Task<string> GetPlaylistFileText(string playlist, bool waitForMinimumSegments)
|
||||
{
|
||||
string fileText;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written
|
||||
using (var fileStream = new FileStream(playlist, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous))
|
||||
{
|
||||
using (var reader = new StreamReader(fileStream))
|
||||
{
|
||||
fileText = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!waitForMinimumSegments || CountStringOccurrences(fileText, "#EXTINF:") >= 3)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
await Task.Delay(25).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// The segement paths within the playlist are phsyical, so strip that out to make it relative
|
||||
fileText = fileText.Replace(Path.GetDirectoryName(playlist) + Path.DirectorySeparatorChar, string.Empty);
|
||||
|
||||
// Even though we specify target duration of 9, ffmpeg seems unable to keep all segments under that amount
|
||||
fileText = fileText.Replace("#EXT-X-TARGETDURATION:9", "#EXT-X-TARGETDURATION:10");
|
||||
|
||||
// It's considered live while still encoding (EVENT). Once the encoding has finished, it's video on demand (VOD).
|
||||
var playlistType = fileText.IndexOf("#EXT-X-ENDLIST", StringComparison.OrdinalIgnoreCase) == -1 ? "EVENT" : "VOD";
|
||||
|
||||
// Add event type at the top
|
||||
fileText = fileText.Replace("#EXT-X-ALLOWCACHE", "#EXT-X-PLAYLIST-TYPE:" + playlistType + Environment.NewLine + "#EXT-X-ALLOWCACHE");
|
||||
|
||||
return fileText;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Count occurrences of strings.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <returns>System.Int32.</returns>
|
||||
private static int CountStringOccurrences(string text, string pattern)
|
||||
{
|
||||
// Loop through all instances of the string 'text'.
|
||||
var count = 0;
|
||||
var i = 0;
|
||||
while ((i = text.IndexOf(pattern, i, StringComparison.OrdinalIgnoreCase)) != -1)
|
||||
{
|
||||
i += pattern.Length;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all command line arguments to pass to ffmpeg
|
||||
/// </summary>
|
||||
/// <param name="outputPath">The playlist output path</param>
|
||||
/// <param name="isoMount">The iso mount.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
protected override string GetCommandLineArguments(string outputPath, IIsoMount isoMount)
|
||||
{
|
||||
var segmentOutputPath = Path.GetDirectoryName(outputPath);
|
||||
var segmentOutputName = HlsSegmentHandler.SegmentFilePrefix + Path.GetFileNameWithoutExtension(outputPath);
|
||||
|
||||
segmentOutputPath = Path.Combine(segmentOutputPath, segmentOutputName + "%03d." + SegmentFileExtension.TrimStart('.'));
|
||||
|
||||
var probeSize = Kernel.FFMpegManager.GetProbeSizeArgument(LibraryItem);
|
||||
|
||||
return string.Format("{0} {1} -i {2}{3} -threads 0 {4} {5} {6} -f ssegment -segment_list_flags +live -segment_time 9 -segment_list \"{7}\" \"{8}\"",
|
||||
probeSize,
|
||||
FastSeekCommandLineParameter,
|
||||
GetInputArgument(isoMount),
|
||||
SlowSeekCommandLineParameter,
|
||||
MapArgs,
|
||||
GetVideoArguments(),
|
||||
GetAudioArguments(),
|
||||
outputPath,
|
||||
segmentOutputPath
|
||||
).Trim();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the partial stream files.
|
||||
/// </summary>
|
||||
/// <param name="playlistFilePath">The playlist file path.</param>
|
||||
protected override void DeletePartialStreamFiles(string playlistFilePath)
|
||||
{
|
||||
var directory = Path.GetDirectoryName(playlistFilePath);
|
||||
var name = Path.GetFileNameWithoutExtension(playlistFilePath);
|
||||
|
||||
var filesToDelete = Directory.EnumerateFiles(directory, "*", SearchOption.TopDirectoryOnly)
|
||||
.Where(f => f.IndexOf(name, StringComparison.OrdinalIgnoreCase) != -1)
|
||||
.ToList();
|
||||
|
||||
foreach (var file in filesToDelete)
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.Info("Deleting HLS file {0}", file);
|
||||
File.Delete(file);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Logger.ErrorException("Error deleting HLS file {0}", ex, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
152
MediaBrowser.Api/Streaming/BaseProgressiveStreamingHandler.cs
Normal file
152
MediaBrowser.Api/Streaming/BaseProgressiveStreamingHandler.cs
Normal file
@ -0,0 +1,152 @@
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.Streaming
|
||||
{
|
||||
/// <summary>
|
||||
/// Class BaseMediaHandler
|
||||
/// </summary>
|
||||
/// <typeparam name="TBaseItemType">The type of the T base item type.</typeparam>
|
||||
public abstract class BaseProgressiveStreamingHandler<TBaseItemType> : BaseStreamingHandler<TBaseItemType>
|
||||
where TBaseItemType : BaseItem, IHasMediaStreams, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the type of the transcoding job.
|
||||
/// </summary>
|
||||
/// <value>The type of the transcoding job.</value>
|
||||
protected override TranscodingJobType TranscodingJobType
|
||||
{
|
||||
get { return TranscodingJobType.Progressive; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes the request.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The CTX.</param>
|
||||
/// <returns>Task.</returns>
|
||||
public override Task ProcessRequest(HttpListenerContext ctx)
|
||||
{
|
||||
HttpListenerContext = ctx;
|
||||
|
||||
if (!RequiresConversion())
|
||||
{
|
||||
return new StaticFileHandler(Kernel)
|
||||
{
|
||||
Path = LibraryItem.Path
|
||||
|
||||
}.ProcessRequest(ctx);
|
||||
}
|
||||
|
||||
var outputPath = OutputFilePath;
|
||||
|
||||
if (File.Exists(outputPath) && !Plugin.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive))
|
||||
{
|
||||
return new StaticFileHandler(Kernel)
|
||||
{
|
||||
Path = outputPath
|
||||
|
||||
}.ProcessRequest(ctx);
|
||||
}
|
||||
|
||||
return base.ProcessRequest(ctx);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Requireses the conversion.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
|
||||
protected bool RequiresConversion()
|
||||
{
|
||||
return !GetBoolQueryStringParam("static");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the response to output stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream.</param>
|
||||
/// <param name="responseInfo">The response info.</param>
|
||||
/// <param name="contentLength">Length of the content.</param>
|
||||
/// <returns>Task.</returns>
|
||||
protected override async Task WriteResponseToOutputStream(Stream stream, ResponseInfo responseInfo, long? contentLength)
|
||||
{
|
||||
// Use the command line args with a dummy playlist path
|
||||
var outputPath = OutputFilePath;
|
||||
|
||||
if (!File.Exists(outputPath))
|
||||
{
|
||||
await StartFFMpeg(outputPath).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
Plugin.Instance.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await StreamFile(outputPath, stream).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error streaming media", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Plugin.Instance.OnTranscodeEndRequest(outputPath, TranscodingJobType.Progressive);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Streams the file.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="outputStream">The output stream.</param>
|
||||
/// <returns>Task{System.Boolean}.</returns>
|
||||
private async Task StreamFile(string path, Stream outputStream)
|
||||
{
|
||||
var eofCount = 0;
|
||||
long position = 0;
|
||||
|
||||
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous))
|
||||
{
|
||||
while (eofCount < 15)
|
||||
{
|
||||
await fs.CopyToAsync(outputStream).ConfigureAwait(false);
|
||||
|
||||
var fsPosition = fs.Position;
|
||||
|
||||
var bytesRead = fsPosition - position;
|
||||
|
||||
//Logger.LogInfo("Streamed {0} bytes from file {1}", bytesRead, path);
|
||||
|
||||
if (bytesRead == 0)
|
||||
{
|
||||
eofCount++;
|
||||
await Task.Delay(100).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
eofCount = 0;
|
||||
}
|
||||
|
||||
position = fsPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the partial stream files.
|
||||
/// </summary>
|
||||
/// <param name="outputFilePath">The output file path.</param>
|
||||
protected override void DeletePartialStreamFiles(string outputFilePath)
|
||||
{
|
||||
File.Delete(outputFilePath);
|
||||
}
|
||||
}
|
||||
}
|
992
MediaBrowser.Api/Streaming/BaseStreamingHandler.cs
Normal file
992
MediaBrowser.Api/Streaming/BaseStreamingHandler.cs
Normal file
@ -0,0 +1,992 @@
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Drawing;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.Streaming
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a common base class for both progressive and hls streaming
|
||||
/// </summary>
|
||||
/// <typeparam name="TBaseItemType">The type of the T base item type.</typeparam>
|
||||
public abstract class BaseStreamingHandler<TBaseItemType> : BaseHandler<Kernel>
|
||||
where TBaseItemType : BaseItem, IHasMediaStreams, new()
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the command line arguments.
|
||||
/// </summary>
|
||||
/// <param name="outputPath">The output path.</param>
|
||||
/// <param name="isoMount">The iso mount.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
protected abstract string GetCommandLineArguments(string outputPath, IIsoMount isoMount);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log file stream.
|
||||
/// </summary>
|
||||
/// <value>The log file stream.</value>
|
||||
protected Stream LogFileStream { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the transcoding job.
|
||||
/// </summary>
|
||||
/// <value>The type of the transcoding job.</value>
|
||||
protected abstract TranscodingJobType TranscodingJobType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the output file extension.
|
||||
/// </summary>
|
||||
/// <value>The output file extension.</value>
|
||||
protected string OutputFileExtension
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.GetExtension(HttpListenerContext.Request.Url.LocalPath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the output file path.
|
||||
/// </summary>
|
||||
/// <value>The output file path.</value>
|
||||
protected string OutputFilePath
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(Kernel.ApplicationPaths.FFMpegStreamCachePath, GetCommandLineArguments("dummy\\dummy", null).GetMD5() + OutputFileExtension.ToLower());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the audio codec to endoce to.
|
||||
/// </summary>
|
||||
/// <value>The audio encoding format.</value>
|
||||
protected virtual AudioCodecs? AudioCodec
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(QueryString["audioCodec"]))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return (AudioCodecs)Enum.Parse(typeof(AudioCodecs), QueryString["audioCodec"], true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the video encoding codec.
|
||||
/// </summary>
|
||||
/// <value>The video codec.</value>
|
||||
protected VideoCodecs? VideoCodec
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(QueryString["videoCodec"]))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return (VideoCodecs)Enum.Parse(typeof(VideoCodecs), QueryString["videoCodec"], true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time, in ticks, in which playback should start
|
||||
/// </summary>
|
||||
/// <value>The start time ticks.</value>
|
||||
protected long? StartTimeTicks
|
||||
{
|
||||
get
|
||||
{
|
||||
string val = QueryString["StartTimeTicks"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return long.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The fast seek offset seconds
|
||||
/// </summary>
|
||||
private const int FastSeekOffsetSeconds = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the fast seek command line parameter.
|
||||
/// </summary>
|
||||
/// <value>The fast seek command line parameter.</value>
|
||||
protected string FastSeekCommandLineParameter
|
||||
{
|
||||
get
|
||||
{
|
||||
var time = StartTimeTicks;
|
||||
|
||||
if (time.HasValue)
|
||||
{
|
||||
var seconds = TimeSpan.FromTicks(time.Value).TotalSeconds - FastSeekOffsetSeconds;
|
||||
|
||||
if (seconds > 0)
|
||||
{
|
||||
return string.Format("-ss {0}", seconds);
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the slow seek command line parameter.
|
||||
/// </summary>
|
||||
/// <value>The slow seek command line parameter.</value>
|
||||
protected string SlowSeekCommandLineParameter
|
||||
{
|
||||
get
|
||||
{
|
||||
var time = StartTimeTicks;
|
||||
|
||||
if (time.HasValue)
|
||||
{
|
||||
if (TimeSpan.FromTicks(time.Value).TotalSeconds - FastSeekOffsetSeconds > 0)
|
||||
{
|
||||
return string.Format(" -ss {0}", FastSeekOffsetSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the map args.
|
||||
/// </summary>
|
||||
/// <value>The map args.</value>
|
||||
protected virtual string MapArgs
|
||||
{
|
||||
get
|
||||
{
|
||||
var args = string.Empty;
|
||||
|
||||
if (VideoStream != null)
|
||||
{
|
||||
args += string.Format("-map 0:{0}", VideoStream.Index);
|
||||
}
|
||||
else
|
||||
{
|
||||
args += "-map -0:v";
|
||||
}
|
||||
|
||||
if (AudioStream != null)
|
||||
{
|
||||
args += string.Format(" -map 0:{0}", AudioStream.Index);
|
||||
}
|
||||
else
|
||||
{
|
||||
args += " -map -0:a";
|
||||
}
|
||||
|
||||
if (SubtitleStream == null)
|
||||
{
|
||||
args += " -map -0:s";
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The _library item
|
||||
/// </summary>
|
||||
private TBaseItemType _libraryItem;
|
||||
/// <summary>
|
||||
/// Gets the library item that will be played, if any
|
||||
/// </summary>
|
||||
/// <value>The library item.</value>
|
||||
protected TBaseItemType LibraryItem
|
||||
{
|
||||
get
|
||||
{
|
||||
return _libraryItem ?? (_libraryItem = (TBaseItemType)DtoBuilder.GetItemByClientId(QueryString["id"]));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the iso mount.
|
||||
/// </summary>
|
||||
/// <value>The iso mount.</value>
|
||||
private IIsoMount IsoMount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The _audio stream
|
||||
/// </summary>
|
||||
private MediaStream _audioStream;
|
||||
/// <summary>
|
||||
/// Gets the audio stream.
|
||||
/// </summary>
|
||||
/// <value>The audio stream.</value>
|
||||
protected MediaStream AudioStream
|
||||
{
|
||||
get { return _audioStream ?? (_audioStream = GetMediaStream(AudioStreamIndex, MediaStreamType.Audio)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The _video stream
|
||||
/// </summary>
|
||||
private MediaStream _videoStream;
|
||||
/// <summary>
|
||||
/// Gets the video stream.
|
||||
/// </summary>
|
||||
/// <value>The video stream.</value>
|
||||
protected MediaStream VideoStream
|
||||
{
|
||||
get
|
||||
{
|
||||
// No video streams here
|
||||
// Need to make this check to make sure we don't pickup embedded image streams (which are listed in the file as type video)
|
||||
if (LibraryItem is Audio)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _videoStream ?? (_videoStream = GetMediaStream(VideoStreamIndex, MediaStreamType.Video));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The subtitle stream
|
||||
/// </summary>
|
||||
private MediaStream _subtitleStream;
|
||||
/// <summary>
|
||||
/// Gets the subtitle stream.
|
||||
/// </summary>
|
||||
/// <value>The subtitle stream.</value>
|
||||
protected MediaStream SubtitleStream
|
||||
{
|
||||
get
|
||||
{
|
||||
// No subtitle streams here
|
||||
if (LibraryItem is Audio)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _subtitleStream ?? (_subtitleStream = GetMediaStream(SubtitleStreamIndex, MediaStreamType.Subtitle, false));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines which stream will be used for playback
|
||||
/// </summary>
|
||||
/// <param name="desiredIndex">Index of the desired.</param>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="returnFirstIfNoIndex">if set to <c>true</c> [return first if no index].</param>
|
||||
/// <returns>MediaStream.</returns>
|
||||
private MediaStream GetMediaStream(int? desiredIndex, MediaStreamType type, bool returnFirstIfNoIndex = true)
|
||||
{
|
||||
var streams = LibraryItem.MediaStreams.Where(s => s.Type == type).ToList();
|
||||
|
||||
if (desiredIndex.HasValue)
|
||||
{
|
||||
var stream = streams.FirstOrDefault(s => s.Index == desiredIndex.Value);
|
||||
|
||||
if (stream != null)
|
||||
{
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
|
||||
// Just return the first one
|
||||
return returnFirstIfNoIndex ? streams.FirstOrDefault() : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the response info.
|
||||
/// </summary>
|
||||
/// <returns>Task{ResponseInfo}.</returns>
|
||||
protected override Task<ResponseInfo> GetResponseInfo()
|
||||
{
|
||||
var info = new ResponseInfo
|
||||
{
|
||||
ContentType = MimeTypes.GetMimeType(OutputFilePath),
|
||||
CompressResponse = false
|
||||
};
|
||||
|
||||
return Task.FromResult(info);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the client's desired audio bitrate
|
||||
/// </summary>
|
||||
/// <value>The audio bit rate.</value>
|
||||
protected int? AudioBitRate
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = QueryString["AudioBitRate"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the client's desired video bitrate
|
||||
/// </summary>
|
||||
/// <value>The video bit rate.</value>
|
||||
protected int? VideoBitRate
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = QueryString["VideoBitRate"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the desired audio stream index
|
||||
/// </summary>
|
||||
/// <value>The index of the audio stream.</value>
|
||||
private int? AudioStreamIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = QueryString["AudioStreamIndex"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the desired video stream index
|
||||
/// </summary>
|
||||
/// <value>The index of the video stream.</value>
|
||||
private int? VideoStreamIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = QueryString["VideoStreamIndex"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the desired subtitle stream index
|
||||
/// </summary>
|
||||
/// <value>The index of the subtitle stream.</value>
|
||||
private int? SubtitleStreamIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = QueryString["SubtitleStreamIndex"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the audio channels.
|
||||
/// </summary>
|
||||
/// <value>The audio channels.</value>
|
||||
public int? AudioChannels
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = QueryString["audiochannels"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the audio sample rate.
|
||||
/// </summary>
|
||||
/// <value>The audio sample rate.</value>
|
||||
public int? AudioSampleRate
|
||||
{
|
||||
get
|
||||
{
|
||||
var val = QueryString["audiosamplerate"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return 44100;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If we're going to put a fixed size on the command line, this will calculate it
|
||||
/// </summary>
|
||||
/// <param name="outputVideoCodec">The output video codec.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
protected string GetOutputSizeParam(string outputVideoCodec)
|
||||
{
|
||||
// http://sonnati.wordpress.com/2012/10/19/ffmpeg-the-swiss-army-knife-of-internet-streaming-part-vi/
|
||||
|
||||
var assSubtitleParam = string.Empty;
|
||||
|
||||
if (SubtitleStream != null)
|
||||
{
|
||||
if (SubtitleStream.Codec.IndexOf("srt", StringComparison.OrdinalIgnoreCase) != -1 || SubtitleStream.Codec.IndexOf("subrip", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
assSubtitleParam = GetTextSubtitleParam(SubtitleStream);
|
||||
}
|
||||
}
|
||||
|
||||
// If fixed dimensions were supplied
|
||||
if (Width.HasValue && Height.HasValue)
|
||||
{
|
||||
return string.Format(" -vf \"scale={0}:{1}{2}\"", Width.Value, Height.Value, assSubtitleParam);
|
||||
}
|
||||
|
||||
var isH264Output = outputVideoCodec.Equals("libx264", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
// If a fixed width was requested
|
||||
if (Width.HasValue)
|
||||
{
|
||||
return isH264Output ?
|
||||
string.Format(" -vf \"scale={0}:trunc(ow/a/2)*2{1}\"", Width.Value, assSubtitleParam) :
|
||||
string.Format(" -vf \"scale={0}:-1{1}\"", Width.Value, assSubtitleParam);
|
||||
}
|
||||
|
||||
// If a max width was requested
|
||||
if (MaxWidth.HasValue && !MaxHeight.HasValue)
|
||||
{
|
||||
return isH264Output ?
|
||||
string.Format(" -vf \"scale=min(iw\\,{0}):trunc(ow/a/2)*2{1}\"", MaxWidth.Value, assSubtitleParam) :
|
||||
string.Format(" -vf \"scale=min(iw\\,{0}):-1{1}\"", MaxWidth.Value, assSubtitleParam);
|
||||
}
|
||||
|
||||
// Need to perform calculations manually
|
||||
|
||||
// Try to account for bad media info
|
||||
var currentHeight = VideoStream.Height ?? MaxHeight ?? Height ?? 0;
|
||||
var currentWidth = VideoStream.Width ?? MaxWidth ?? Width ?? 0;
|
||||
|
||||
var outputSize = DrawingUtils.Resize(currentWidth, currentHeight, Width, Height, MaxWidth, MaxHeight);
|
||||
|
||||
// If we're encoding with libx264, it can't handle odd numbered widths or heights, so we'll have to fix that
|
||||
if (isH264Output)
|
||||
{
|
||||
return string.Format(" -vf \"scale=trunc({0}/2)*2:trunc({1}/2)*2{2}\"", outputSize.Width, outputSize.Height, assSubtitleParam);
|
||||
}
|
||||
|
||||
// Otherwise use -vf scale since ffmpeg will ensure internally that the aspect ratio is preserved
|
||||
return string.Format(" -vf \"scale={0}:-1{1}\"", Convert.ToInt32(outputSize.Width), assSubtitleParam);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the text subtitle param.
|
||||
/// </summary>
|
||||
/// <param name="subtitleStream">The subtitle stream.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
protected string GetTextSubtitleParam(MediaStream subtitleStream)
|
||||
{
|
||||
var path = subtitleStream.IsExternal ? GetConvertedAssPath(subtitleStream) : GetExtractedAssPath(subtitleStream);
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var param = string.Format(",ass={0}", path);
|
||||
|
||||
var time = StartTimeTicks;
|
||||
|
||||
if (time.HasValue)
|
||||
{
|
||||
var seconds = Convert.ToInt32(TimeSpan.FromTicks(time.Value).TotalSeconds);
|
||||
param += string.Format(",setpts=PTS-{0}/TB", seconds);
|
||||
}
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the extracted ass path.
|
||||
/// </summary>
|
||||
/// <param name="subtitleStream">The subtitle stream.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
private string GetExtractedAssPath(MediaStream subtitleStream)
|
||||
{
|
||||
var video = LibraryItem as Video;
|
||||
|
||||
var path = Kernel.FFMpegManager.GetSubtitleCachePath(video, subtitleStream.Index, ".ass");
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
var success = Kernel.FFMpegManager.ExtractTextSubtitle(video, subtitleStream.Index, path, CancellationToken.None).Result;
|
||||
|
||||
if (!success)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the converted ass path.
|
||||
/// </summary>
|
||||
/// <param name="subtitleStream">The subtitle stream.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
private string GetConvertedAssPath(MediaStream subtitleStream)
|
||||
{
|
||||
var video = LibraryItem as Video;
|
||||
|
||||
var path = Kernel.FFMpegManager.GetSubtitleCachePath(video, subtitleStream.Index, ".ass");
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
var success = Kernel.FFMpegManager.ConvertTextSubtitle(subtitleStream, path, CancellationToken.None).Result;
|
||||
|
||||
if (!success)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal graphical subtitle param.
|
||||
/// </summary>
|
||||
/// <param name="subtitleStream">The subtitle stream.</param>
|
||||
/// <param name="videoCodec">The video codec.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
protected string GetInternalGraphicalSubtitleParam(MediaStream subtitleStream, string videoCodec)
|
||||
{
|
||||
var outputSizeParam = string.Empty;
|
||||
|
||||
// Add resolution params, if specified
|
||||
if (Width.HasValue || Height.HasValue || MaxHeight.HasValue || MaxWidth.HasValue)
|
||||
{
|
||||
outputSizeParam = GetOutputSizeParam(videoCodec).TrimEnd('"');
|
||||
outputSizeParam = "," + outputSizeParam.Substring(outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
return string.Format(" -filter_complex \"[0:{0}]format=yuva444p,lut=u=128:v=128:y=gammaval(.3)[sub] ; [0:0] [sub] overlay{1}\"", subtitleStream.Index, outputSizeParam);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the fixed output video height, in pixels
|
||||
/// </summary>
|
||||
/// <value>The height.</value>
|
||||
protected int? Height
|
||||
{
|
||||
get
|
||||
{
|
||||
string val = QueryString["height"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the fixed output video width, in pixels
|
||||
/// </summary>
|
||||
/// <value>The width.</value>
|
||||
protected int? Width
|
||||
{
|
||||
get
|
||||
{
|
||||
string val = QueryString["width"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum output video height, in pixels
|
||||
/// </summary>
|
||||
/// <value>The height of the max.</value>
|
||||
protected int? MaxHeight
|
||||
{
|
||||
get
|
||||
{
|
||||
string val = QueryString["maxheight"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum output video width, in pixels
|
||||
/// </summary>
|
||||
/// <value>The width of the max.</value>
|
||||
protected int? MaxWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
string val = QueryString["maxwidth"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return int.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the output video framerate
|
||||
/// </summary>
|
||||
/// <value>The max frame rate.</value>
|
||||
protected float? FrameRate
|
||||
{
|
||||
get
|
||||
{
|
||||
string val = QueryString["framerate"];
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return float.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of audio channels to specify on the command line
|
||||
/// </summary>
|
||||
/// <returns>System.Nullable{System.Int32}.</returns>
|
||||
protected int? GetSampleRateParam()
|
||||
{
|
||||
// If the user requested a max value
|
||||
if (AudioSampleRate.HasValue)
|
||||
{
|
||||
return AudioSampleRate.Value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of audio channels to specify on the command line
|
||||
/// </summary>
|
||||
/// <param name="audioCodec">The audio codec.</param>
|
||||
/// <returns>System.Nullable{System.Int32}.</returns>
|
||||
protected int? GetNumAudioChannelsParam(string audioCodec)
|
||||
{
|
||||
if (AudioStream.Channels > 2)
|
||||
{
|
||||
if (audioCodec.Equals("libvo_aacenc"))
|
||||
{
|
||||
// libvo_aacenc currently only supports two channel output
|
||||
return 2;
|
||||
}
|
||||
if (audioCodec.Equals("wmav2"))
|
||||
{
|
||||
// wmav2 currently only supports two channel output
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
return GetNumAudioChannelsParam();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of audio channels to specify on the command line
|
||||
/// </summary>
|
||||
/// <returns>System.Nullable{System.Int32}.</returns>
|
||||
protected int? GetNumAudioChannelsParam()
|
||||
{
|
||||
// If the user requested a max number of channels
|
||||
if (AudioChannels.HasValue)
|
||||
{
|
||||
return AudioChannels.Value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified stream is H264.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream.</param>
|
||||
/// <returns><c>true</c> if the specified stream is H264; otherwise, <c>false</c>.</returns>
|
||||
protected bool IsH264(MediaStream stream)
|
||||
{
|
||||
return stream.Codec.IndexOf("264", StringComparison.OrdinalIgnoreCase) != -1 ||
|
||||
stream.Codec.IndexOf("avc", StringComparison.OrdinalIgnoreCase) != -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the output audio codec
|
||||
/// </summary>
|
||||
/// <returns>System.String.</returns>
|
||||
protected string GetAudioCodec()
|
||||
{
|
||||
if (AudioCodec.HasValue)
|
||||
{
|
||||
if (AudioCodec == AudioCodecs.Aac)
|
||||
{
|
||||
return "libvo_aacenc";
|
||||
}
|
||||
if (AudioCodec == AudioCodecs.Mp3)
|
||||
{
|
||||
return "libmp3lame";
|
||||
}
|
||||
if (AudioCodec == AudioCodecs.Vorbis)
|
||||
{
|
||||
return "libvorbis";
|
||||
}
|
||||
if (AudioCodec == AudioCodecs.Wma)
|
||||
{
|
||||
return "wmav2";
|
||||
}
|
||||
}
|
||||
|
||||
return "copy";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the output video codec
|
||||
/// </summary>
|
||||
/// <returns>System.String.</returns>
|
||||
protected string GetVideoCodec()
|
||||
{
|
||||
if (VideoCodec.HasValue)
|
||||
{
|
||||
if (VideoCodec == VideoCodecs.H264)
|
||||
{
|
||||
return "libx264";
|
||||
}
|
||||
if (VideoCodec == VideoCodecs.Vpx)
|
||||
{
|
||||
return "libvpx";
|
||||
}
|
||||
if (VideoCodec == VideoCodecs.Wmv)
|
||||
{
|
||||
return "wmv2";
|
||||
}
|
||||
if (VideoCodec == VideoCodecs.Theora)
|
||||
{
|
||||
return "libtheora";
|
||||
}
|
||||
}
|
||||
|
||||
return "copy";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the input argument.
|
||||
/// </summary>
|
||||
/// <param name="isoMount">The iso mount.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
protected string GetInputArgument(IIsoMount isoMount)
|
||||
{
|
||||
return isoMount == null ?
|
||||
Kernel.FFMpegManager.GetInputArgument(LibraryItem) :
|
||||
Kernel.FFMpegManager.GetInputArgument(LibraryItem as Video, IsoMount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the FFMPEG.
|
||||
/// </summary>
|
||||
/// <param name="outputPath">The output path.</param>
|
||||
/// <returns>Task.</returns>
|
||||
protected async Task StartFFMpeg(string outputPath)
|
||||
{
|
||||
var video = LibraryItem as Video;
|
||||
|
||||
if (video != null && video.VideoType == VideoType.Iso &&
|
||||
video.IsoType.HasValue && Kernel.IsoManager.CanMount(video.Path))
|
||||
{
|
||||
IsoMount = await Kernel.IsoManager.Mount(video.Path, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var process = new Process
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
CreateNoWindow = true,
|
||||
UseShellExecute = false,
|
||||
|
||||
// Must consume both stdout and stderr or deadlocks may occur
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
|
||||
FileName = Kernel.FFMpegManager.FFMpegPath,
|
||||
WorkingDirectory = Path.GetDirectoryName(Kernel.FFMpegManager.FFMpegPath),
|
||||
Arguments = GetCommandLineArguments(outputPath, IsoMount),
|
||||
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
ErrorDialog = false
|
||||
},
|
||||
|
||||
EnableRaisingEvents = true
|
||||
};
|
||||
|
||||
Plugin.Instance.OnTranscodeBeginning(outputPath, TranscodingJobType, process);
|
||||
|
||||
Logger.Info(process.StartInfo.FileName + " " + process.StartInfo.Arguments);
|
||||
|
||||
var logFilePath = Path.Combine(Kernel.ApplicationPaths.LogDirectoryPath, "ffmpeg-" + Guid.NewGuid() + ".txt");
|
||||
|
||||
// FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
|
||||
LogFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous);
|
||||
|
||||
process.Exited += OnFFMpegProcessExited;
|
||||
|
||||
try
|
||||
{
|
||||
process.Start();
|
||||
}
|
||||
catch (Win32Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error starting ffmpeg", ex);
|
||||
|
||||
Plugin.Instance.OnTranscodeFailedToStart(outputPath, TranscodingJobType);
|
||||
|
||||
process.Exited -= OnFFMpegProcessExited;
|
||||
LogFileStream.Dispose();
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
// MUST read both stdout and stderr asynchronously or a deadlock may occurr
|
||||
process.BeginOutputReadLine();
|
||||
|
||||
// Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
|
||||
process.StandardError.BaseStream.CopyToAsync(LogFileStream);
|
||||
|
||||
// Wait for the file to exist before proceeeding
|
||||
while (!File.Exists(outputPath))
|
||||
{
|
||||
await Task.Delay(100).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes the exited.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sender.</param>
|
||||
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
|
||||
protected void OnFFMpegProcessExited(object sender, EventArgs e)
|
||||
{
|
||||
if (IsoMount != null)
|
||||
{
|
||||
IsoMount.Dispose();
|
||||
IsoMount = null;
|
||||
}
|
||||
|
||||
var outputFilePath = OutputFilePath;
|
||||
|
||||
LogFileStream.Dispose();
|
||||
|
||||
var process = (Process)sender;
|
||||
|
||||
process.Exited -= OnFFMpegProcessExited;
|
||||
|
||||
int? exitCode = null;
|
||||
|
||||
try
|
||||
{
|
||||
exitCode = process.ExitCode;
|
||||
Logger.Info("FFMpeg exited with code {0} for {1}", exitCode.Value, outputFilePath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.Info("FFMpeg exited with an error for {0}", outputFilePath);
|
||||
}
|
||||
|
||||
process.Dispose();
|
||||
|
||||
Plugin.Instance.OnTranscodingFinished(outputFilePath, TranscodingJobType);
|
||||
|
||||
if (!exitCode.HasValue || exitCode.Value != 0)
|
||||
{
|
||||
Logger.Info("Deleting partial stream file(s) {0}", outputFilePath);
|
||||
|
||||
try
|
||||
{
|
||||
DeletePartialStreamFiles(outputFilePath);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Logger.ErrorException("Error deleting partial stream file(s) {0}", ex, outputFilePath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info("FFMpeg completed and exited normally for {0}", outputFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the partial stream files.
|
||||
/// </summary>
|
||||
/// <param name="outputFilePath">The output file path.</param>
|
||||
protected abstract void DeletePartialStreamFiles(string outputFilePath);
|
||||
}
|
||||
}
|
102
MediaBrowser.Api/Streaming/HlsAudioPlaylistHandler.cs
Normal file
102
MediaBrowser.Api/Streaming/HlsAudioPlaylistHandler.cs
Normal file
@ -0,0 +1,102 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Net;
|
||||
|
||||
namespace MediaBrowser.Api.Streaming
|
||||
{
|
||||
/// <summary>
|
||||
/// Class HlsAudioPlaylistHandler
|
||||
/// </summary>
|
||||
[Export(typeof(IHttpServerHandler))]
|
||||
public class HlsAudioPlaylistHandler : BaseHlsPlaylistHandler<Audio>
|
||||
{
|
||||
/// <summary>
|
||||
/// Handleses the request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("audio.m3u8", request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the segment file extension.
|
||||
/// </summary>
|
||||
/// <value>The segment file extension.</value>
|
||||
/// <exception cref="InvalidOperationException">Only aac and mp3 audio codecs are supported.</exception>
|
||||
protected override string SegmentFileExtension
|
||||
{
|
||||
get
|
||||
{
|
||||
if (AudioCodec == AudioCodecs.Aac)
|
||||
{
|
||||
return ".aac";
|
||||
}
|
||||
if (AudioCodec == AudioCodecs.Mp3)
|
||||
{
|
||||
return ".mp3";
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Only aac and mp3 audio codecs are supported.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the video arguments.
|
||||
/// </summary>
|
||||
/// <returns>System.String.</returns>
|
||||
protected override string GetVideoArguments()
|
||||
{
|
||||
// No video
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the map args.
|
||||
/// </summary>
|
||||
/// <value>The map args.</value>
|
||||
protected override string MapArgs
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Format("-map 0:{0}", AudioStream.Index);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the audio arguments.
|
||||
/// </summary>
|
||||
/// <returns>System.String.</returns>
|
||||
protected override string GetAudioArguments()
|
||||
{
|
||||
var codec = GetAudioCodec();
|
||||
|
||||
var args = "-codec:a " + codec;
|
||||
|
||||
var channels = GetNumAudioChannelsParam();
|
||||
|
||||
if (channels.HasValue)
|
||||
{
|
||||
args += " -ac " + channels.Value;
|
||||
}
|
||||
|
||||
var sampleRate = GetSampleRateParam();
|
||||
|
||||
if (sampleRate.HasValue)
|
||||
{
|
||||
args += " -ar " + sampleRate.Value;
|
||||
}
|
||||
|
||||
if (AudioBitRate.HasValue)
|
||||
{
|
||||
args += " -ab " + AudioBitRate.Value;
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
}
|
||||
}
|
87
MediaBrowser.Api/Streaming/HlsSegmentHandler.cs
Normal file
87
MediaBrowser.Api/Streaming/HlsSegmentHandler.cs
Normal file
@ -0,0 +1,87 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.Streaming
|
||||
{
|
||||
/// <summary>
|
||||
/// Class HlsSegmentHandler
|
||||
/// </summary>
|
||||
[Export(typeof(IHttpServerHandler))]
|
||||
public class HlsSegmentHandler : BaseHandler<Kernel>
|
||||
{
|
||||
/// <summary>
|
||||
/// The segment file prefix
|
||||
/// </summary>
|
||||
public const string SegmentFilePrefix = "segment-";
|
||||
|
||||
/// <summary>
|
||||
/// Handleses the request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
const string url = "/api/" + SegmentFilePrefix;
|
||||
|
||||
return request.Url.LocalPath.IndexOf(url, StringComparison.OrdinalIgnoreCase) != -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the response to output stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream.</param>
|
||||
/// <param name="responseInfo">The response info.</param>
|
||||
/// <param name="contentLength">Length of the content.</param>
|
||||
/// <returns>Task.</returns>
|
||||
/// <exception cref="System.NotImplementedException"></exception>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
protected override Task WriteResponseToOutputStream(Stream stream, ResponseInfo responseInfo, long? contentLength)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the response info.
|
||||
/// </summary>
|
||||
/// <returns>Task{ResponseInfo}.</returns>
|
||||
/// <exception cref="System.NotImplementedException"></exception>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
protected override Task<ResponseInfo> GetResponseInfo()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes the request.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The CTX.</param>
|
||||
/// <returns>Task.</returns>
|
||||
public override async Task ProcessRequest(HttpListenerContext ctx)
|
||||
{
|
||||
var path = Path.GetFileName(ctx.Request.Url.LocalPath);
|
||||
|
||||
path = Path.Combine(Kernel.ApplicationPaths.FFMpegStreamCachePath, path);
|
||||
|
||||
var playlistFilename = Path.GetFileNameWithoutExtension(path).Substring(SegmentFilePrefix.Length);
|
||||
playlistFilename = playlistFilename.Substring(0, playlistFilename.Length - 3);
|
||||
|
||||
var playlistPath = Path.Combine(Path.GetDirectoryName(path), playlistFilename + ".m3u8");
|
||||
|
||||
Plugin.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls);
|
||||
|
||||
try
|
||||
{
|
||||
await new StaticFileHandler(Kernel) { Path = path }.ProcessRequest(ctx).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Plugin.Instance.OnTranscodeEndRequest(playlistPath, TranscodingJobType.Hls);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
134
MediaBrowser.Api/Streaming/HlsVideoPlaylistHandler.cs
Normal file
134
MediaBrowser.Api/Streaming/HlsVideoPlaylistHandler.cs
Normal file
@ -0,0 +1,134 @@
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Net;
|
||||
|
||||
namespace MediaBrowser.Api.Streaming
|
||||
{
|
||||
/// <summary>
|
||||
/// Class HlsVideoPlaylistHandler
|
||||
/// </summary>
|
||||
[Export(typeof(IHttpServerHandler))]
|
||||
public class HlsVideoPlaylistHandler : BaseHlsPlaylistHandler<Video>
|
||||
{
|
||||
/// <summary>
|
||||
/// Handleses the request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return ApiService.IsApiUrlMatch("video.m3u8", request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the segment file extension.
|
||||
/// </summary>
|
||||
/// <value>The segment file extension.</value>
|
||||
protected override string SegmentFileExtension
|
||||
{
|
||||
get { return ".ts"; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the video arguments.
|
||||
/// </summary>
|
||||
/// <returns>System.String.</returns>
|
||||
protected override string GetVideoArguments()
|
||||
{
|
||||
var codec = GetVideoCodec();
|
||||
|
||||
// Right now all we support is either h264 or copy
|
||||
if (!codec.Equals("copy", StringComparison.OrdinalIgnoreCase) && !codec.Equals("libx264", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
codec = "libx264";
|
||||
}
|
||||
|
||||
// See if we can save come cpu cycles by avoiding encoding
|
||||
if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return IsH264(VideoStream) ? "-codec:v:0 copy -bsf h264_mp4toannexb" : "-codec:v:0 copy";
|
||||
}
|
||||
|
||||
var args = "-codec:v:0 " + codec + " -preset superfast";
|
||||
|
||||
if (VideoBitRate.HasValue)
|
||||
{
|
||||
args += string.Format(" -b:v {0}", VideoBitRate.Value);
|
||||
}
|
||||
|
||||
// Add resolution params, if specified
|
||||
if (Width.HasValue || Height.HasValue || MaxHeight.HasValue || MaxWidth.HasValue)
|
||||
{
|
||||
args += GetOutputSizeParam(codec);
|
||||
}
|
||||
|
||||
// Get the output framerate based on the FrameRate param
|
||||
double framerate = FrameRate ?? 0;
|
||||
|
||||
// We have to supply a framerate for hls, so if it's null, account for that here
|
||||
if (framerate.Equals(0))
|
||||
{
|
||||
framerate = VideoStream.AverageFrameRate ?? 0;
|
||||
}
|
||||
if (framerate.Equals(0))
|
||||
{
|
||||
framerate = VideoStream.RealFrameRate ?? 0;
|
||||
}
|
||||
if (framerate.Equals(0))
|
||||
{
|
||||
framerate = 23.976;
|
||||
}
|
||||
|
||||
args += string.Format(" -r {0}", framerate);
|
||||
|
||||
// Needed to ensure segments stay under 10 seconds
|
||||
args += string.Format(" -g {0}", framerate);
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the audio arguments to pass to ffmpeg
|
||||
/// </summary>
|
||||
/// <returns>System.String.</returns>
|
||||
protected override string GetAudioArguments()
|
||||
{
|
||||
if (!AudioCodec.HasValue)
|
||||
{
|
||||
return "-codec:a:0 copy";
|
||||
}
|
||||
|
||||
var codec = GetAudioCodec();
|
||||
|
||||
var args = "-codec:a:0 " + codec;
|
||||
|
||||
if (AudioStream != null)
|
||||
{
|
||||
var channels = GetNumAudioChannelsParam();
|
||||
|
||||
if (channels.HasValue)
|
||||
{
|
||||
args += " -ac " + channels.Value;
|
||||
}
|
||||
|
||||
var sampleRate = GetSampleRateParam();
|
||||
|
||||
if (sampleRate.HasValue)
|
||||
{
|
||||
args += " -ar " + sampleRate.Value;
|
||||
}
|
||||
|
||||
if (AudioBitRate.HasValue)
|
||||
{
|
||||
args += " -ab " + AudioBitRate.Value;
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
}
|
||||
}
|
185
MediaBrowser.Api/Streaming/VideoHandler.cs
Normal file
185
MediaBrowser.Api/Streaming/VideoHandler.cs
Normal file
@ -0,0 +1,185 @@
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Common.Net.Handlers;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Resolvers;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
||||
namespace MediaBrowser.Api.Streaming
|
||||
{
|
||||
/// <summary>
|
||||
/// Providers a progressive streaming video api
|
||||
/// </summary>
|
||||
[Export(typeof(IHttpServerHandler))]
|
||||
class VideoHandler : BaseProgressiveStreamingHandler<Video>
|
||||
{
|
||||
/// <summary>
|
||||
/// Handleses the request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
|
||||
public override bool HandlesRequest(HttpListenerRequest request)
|
||||
{
|
||||
return EntityResolutionHelper.VideoFileExtensions.Any(a => ApiService.IsApiUrlMatch("video" + a, request));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates arguments to pass to ffmpeg
|
||||
/// </summary>
|
||||
/// <param name="outputPath">The output path.</param>
|
||||
/// <param name="isoMount">The iso mount.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
protected override string GetCommandLineArguments(string outputPath, IIsoMount isoMount)
|
||||
{
|
||||
var probeSize = Kernel.FFMpegManager.GetProbeSizeArgument(LibraryItem.VideoType, LibraryItem.IsoType);
|
||||
|
||||
// Get the output codec name
|
||||
var videoCodec = GetVideoCodec();
|
||||
|
||||
var graphicalSubtitleParam = string.Empty;
|
||||
|
||||
if (SubtitleStream != null)
|
||||
{
|
||||
// This is for internal graphical subs
|
||||
if (!SubtitleStream.IsExternal && (SubtitleStream.Codec.IndexOf("pgs", StringComparison.OrdinalIgnoreCase) != -1 || SubtitleStream.Codec.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) != -1))
|
||||
{
|
||||
graphicalSubtitleParam = GetInternalGraphicalSubtitleParam(SubtitleStream, videoCodec);
|
||||
}
|
||||
}
|
||||
|
||||
return string.Format("{0} {1} -i {2}{3} -threads 0 {4} {5}{6} {7} \"{8}\"",
|
||||
probeSize,
|
||||
FastSeekCommandLineParameter,
|
||||
GetInputArgument(isoMount),
|
||||
SlowSeekCommandLineParameter,
|
||||
MapArgs,
|
||||
GetVideoArguments(videoCodec),
|
||||
graphicalSubtitleParam,
|
||||
GetAudioArguments(),
|
||||
outputPath
|
||||
).Trim();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets video arguments to pass to ffmpeg
|
||||
/// </summary>
|
||||
/// <returns>System.String.</returns>
|
||||
private string GetVideoArguments(string videoCodec)
|
||||
{
|
||||
var args = "-vcodec " + videoCodec;
|
||||
|
||||
// If we're encoding video, add additional params
|
||||
if (!videoCodec.Equals("copy", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Add resolution params, if specified
|
||||
if (Width.HasValue || Height.HasValue || MaxHeight.HasValue || MaxWidth.HasValue)
|
||||
{
|
||||
args += GetOutputSizeParam(videoCodec);
|
||||
}
|
||||
|
||||
if (FrameRate.HasValue)
|
||||
{
|
||||
args += string.Format(" -r {0}", FrameRate.Value);
|
||||
}
|
||||
|
||||
// Add the audio bitrate
|
||||
var qualityParam = GetVideoQualityParam(videoCodec);
|
||||
|
||||
if (!string.IsNullOrEmpty(qualityParam))
|
||||
{
|
||||
args += " " + qualityParam;
|
||||
}
|
||||
}
|
||||
else if (IsH264(VideoStream))
|
||||
{
|
||||
args += " -bsf h264_mp4toannexb";
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets audio arguments to pass to ffmpeg
|
||||
/// </summary>
|
||||
/// <returns>System.String.</returns>
|
||||
private string GetAudioArguments()
|
||||
{
|
||||
// If the video doesn't have an audio stream, return a default.
|
||||
if (AudioStream == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// Get the output codec name
|
||||
var codec = GetAudioCodec();
|
||||
|
||||
var args = "-acodec " + codec;
|
||||
|
||||
// If we're encoding audio, add additional params
|
||||
if (!codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Add the number of audio channels
|
||||
var channels = GetNumAudioChannelsParam();
|
||||
|
||||
if (channels.HasValue)
|
||||
{
|
||||
args += " -ac " + channels.Value;
|
||||
}
|
||||
|
||||
// Add the audio sample rate
|
||||
var sampleRate = GetSampleRateParam();
|
||||
|
||||
if (sampleRate.HasValue)
|
||||
{
|
||||
args += " -ar " + sampleRate.Value;
|
||||
}
|
||||
|
||||
if (AudioBitRate.HasValue)
|
||||
{
|
||||
args += " -ab " + AudioBitRate.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the video bitrate to specify on the command line
|
||||
/// </summary>
|
||||
/// <param name="videoCodec">The video codec.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
private string GetVideoQualityParam(string videoCodec)
|
||||
{
|
||||
var args = string.Empty;
|
||||
|
||||
// webm
|
||||
if (videoCodec.Equals("libvpx", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
args = "-g 120 -cpu-used 1 -lag-in-frames 16 -deadline realtime -slices 4 -vprofile 0";
|
||||
}
|
||||
|
||||
// asf/wmv
|
||||
else if (videoCodec.Equals("wmv2", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
args = "-g 100 -qmax 15";
|
||||
}
|
||||
|
||||
else if (videoCodec.Equals("libx264", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
args = "-preset superfast";
|
||||
}
|
||||
|
||||
if (VideoBitRate.HasValue)
|
||||
{
|
||||
args += " -b:v " + VideoBitRate;
|
||||
}
|
||||
|
||||
return args.Trim();
|
||||
}
|
||||
}
|
||||
}
|
107
MediaBrowser.Api/SystemService.cs
Normal file
107
MediaBrowser.Api/SystemService.cs
Normal file
@ -0,0 +1,107 @@
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Serialization;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.System;
|
||||
using ServiceStack.ServiceHost;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
{
|
||||
[Route("/System/Info", "GET")]
|
||||
public class GetSystemInfo : IReturn<SystemInfo>
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class RestartApplication
|
||||
/// </summary>
|
||||
[Route("/System/Restart", "POST")]
|
||||
[ServiceStack.ServiceHost.Api(("Restarts the application, if needed"))]
|
||||
public class RestartApplication
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetConfiguration
|
||||
/// </summary>
|
||||
[Route("/System/Configuration", "GET")]
|
||||
public class GetConfiguration : IReturn<ServerConfiguration>
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class UpdateConfiguration
|
||||
/// </summary>
|
||||
[Route("/System/Configuration", "POST")]
|
||||
public class UpdateConfiguration : IRequiresRequestStream
|
||||
{
|
||||
public Stream RequestStream { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class SystemInfoService
|
||||
/// </summary>
|
||||
[Export(typeof(IRestfulService))]
|
||||
public class SystemService : BaseRestService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetSystemInfo request)
|
||||
{
|
||||
var result = Kernel.GetSystemInfo();
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetConfiguration request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var dateModified = File.GetLastWriteTimeUtc(Kernel.ApplicationPaths.SystemConfigurationFilePath);
|
||||
|
||||
var cacheKey = (Kernel.ApplicationPaths.SystemConfigurationFilePath + dateModified.Ticks).GetMD5();
|
||||
|
||||
return ToOptimizedResultUsingCache(cacheKey, dateModified, null, () => kernel.Configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public void Post(RestartApplication request)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(100);
|
||||
Kernel.PerformPendingRestart();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts the specified configuraiton.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public void Post(UpdateConfiguration request)
|
||||
{
|
||||
var serverConfig = JsonSerializer.DeserializeFromStream<ServerConfiguration>(request.RequestStream);
|
||||
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
kernel.UpdateConfiguration(serverConfig);
|
||||
}
|
||||
}
|
||||
}
|
161
MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
Normal file
161
MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
Normal file
@ -0,0 +1,161 @@
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using ServiceStack.ServiceHost;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.UserLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// Class BaseItemsByNameService
|
||||
/// </summary>
|
||||
/// <typeparam name="TItemType">The type of the T item type.</typeparam>
|
||||
public abstract class BaseItemsByNameService<TItemType> : BaseRestService
|
||||
where TItemType : BaseItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>Task{ItemsResult}.</returns>
|
||||
protected async Task<ItemsResult> GetResult(GetItemsByName request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var user = kernel.GetUserById(request.UserId);
|
||||
|
||||
var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.Id, user.Id);
|
||||
|
||||
IEnumerable<BaseItem> items;
|
||||
|
||||
if (item.IsFolder)
|
||||
{
|
||||
var folder = (Folder)item;
|
||||
|
||||
items = request.Recursive ? folder.GetRecursiveChildren(user) : folder.GetChildren(user);
|
||||
}
|
||||
else
|
||||
{
|
||||
items = new[] { item };
|
||||
}
|
||||
|
||||
var ibnItemsArray = GetAllItems(request, items, user).ToArray();
|
||||
IEnumerable<Tuple<string, Func<int>>> ibnItems = ibnItemsArray;
|
||||
|
||||
var result = new ItemsResult
|
||||
{
|
||||
TotalRecordCount = ibnItemsArray.Length
|
||||
};
|
||||
|
||||
if (request.StartIndex.HasValue || request.PageSize.HasValue)
|
||||
{
|
||||
if (request.StartIndex.HasValue)
|
||||
{
|
||||
ibnItems = ibnItems.Skip(request.StartIndex.Value);
|
||||
}
|
||||
|
||||
if (request.PageSize.HasValue)
|
||||
{
|
||||
ibnItems = ibnItems.Take(request.PageSize.Value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var tasks = ibnItems.Select(i => GetDto(i, user, new List<ItemFields>()));
|
||||
|
||||
var resultItems = await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
|
||||
result.Items = resultItems.Where(i => i != null).OrderByDescending(i => i.SortName ?? i.Name).ToArray();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all items.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
|
||||
protected abstract IEnumerable<Tuple<string, Func<int>>> GetAllItems(GetItemsByName request, IEnumerable<BaseItem> items, User user);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the entity.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <returns>Task{BaseItem}.</returns>
|
||||
protected abstract Task<TItemType> GetEntity(string name);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the dto.
|
||||
/// </summary>
|
||||
/// <param name="stub">The stub.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="fields">The fields.</param>
|
||||
/// <returns>Task{DtoBaseItem}.</returns>
|
||||
private async Task<DtoBaseItem> GetDto(Tuple<string, Func<int>> stub, User user, List<ItemFields> fields)
|
||||
{
|
||||
BaseItem item;
|
||||
|
||||
try
|
||||
{
|
||||
item = await GetEntity(stub.Item1).ConfigureAwait(false);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Logger.ErrorException("Error getting IBN item {0}", ex, stub.Item1);
|
||||
return null;
|
||||
}
|
||||
|
||||
var dto = await DtoBuilder.GetDtoBaseItem(item, user, fields).ConfigureAwait(false);
|
||||
|
||||
dto.ChildCount = stub.Item2();
|
||||
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetItemsByName
|
||||
/// </summary>
|
||||
public class GetItemsByName : IReturn<ItemsResult>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
public Guid UserId { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the start index.
|
||||
/// </summary>
|
||||
/// <value>The start index.</value>
|
||||
public int? StartIndex { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the size of the page.
|
||||
/// </summary>
|
||||
/// <value>The size of the page.</value>
|
||||
public int? PageSize { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this <see cref="GetItemsByName" /> is recursive.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if recursive; otherwise, <c>false</c>.</value>
|
||||
public bool Recursive { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the sort order.
|
||||
/// </summary>
|
||||
/// <value>The sort order.</value>
|
||||
public SortOrder? SortOrder { get; set; }
|
||||
/// <summary>
|
||||
/// If specified the search will be localized within a specific item or folder
|
||||
/// </summary>
|
||||
/// <value>The item id.</value>
|
||||
public string Id { get; set; }
|
||||
}
|
||||
}
|
69
MediaBrowser.Api/UserLibrary/GenresService.cs
Normal file
69
MediaBrowser.Api/UserLibrary/GenresService.cs
Normal file
@ -0,0 +1,69 @@
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using ServiceStack.ServiceHost;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.UserLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetGenres
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/Items/{Id}/Genres", "GET")]
|
||||
[Route("/Users/{UserId}/Items/Root/Genres", "GET")]
|
||||
public class GetGenres : GetItemsByName
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GenresService
|
||||
/// </summary>
|
||||
[Export(typeof(IRestfulService))]
|
||||
public class GenresService : BaseItemsByNameService<Genre>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetGenres request)
|
||||
{
|
||||
var result = GetResult(request).Result;
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all items.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
|
||||
protected override IEnumerable<Tuple<string, Func<int>>> GetAllItems(GetItemsByName request, IEnumerable<BaseItem> items, User user)
|
||||
{
|
||||
var itemsList = items.Where(i => i.Genres != null).ToList();
|
||||
|
||||
return itemsList
|
||||
.SelectMany(i => i.Genres)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.Select(name => new Tuple<string, Func<int>>(name, () => itemsList.Count(i => i.Genres.Contains(name, StringComparer.OrdinalIgnoreCase))));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the entity.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <returns>Task{Genre}.</returns>
|
||||
protected override Task<Genre> GetEntity(string name)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
return kernel.LibraryManager.GetGenre(name);
|
||||
}
|
||||
}
|
||||
}
|
788
MediaBrowser.Api/UserLibrary/ItemsService.cs
Normal file
788
MediaBrowser.Api/UserLibrary/ItemsService.cs
Normal file
@ -0,0 +1,788 @@
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using ServiceStack.ServiceHost;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.UserLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetItems
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/Items", "GET")]
|
||||
public class GetItems : IReturn<ItemsResult>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specify this to localize the search to a specific item or folder. Omit to use the root.
|
||||
/// </summary>
|
||||
/// <value>The parent id.</value>
|
||||
public string ParentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Skips over a given number of items within the results. Use for paging.
|
||||
/// </summary>
|
||||
/// <value>The start index.</value>
|
||||
public int? StartIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of items to return
|
||||
/// </summary>
|
||||
/// <value>The limit.</value>
|
||||
public int? Limit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not to perform the query recursively
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if recursive; otherwise, <c>false</c>.</value>
|
||||
public bool Recursive { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Limit results to items containing a specific person
|
||||
/// </summary>
|
||||
/// <value>The person.</value>
|
||||
public string Person { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If the Person filter is used, this can also be used to restrict to a specific person type
|
||||
/// </summary>
|
||||
/// <value>The type of the person.</value>
|
||||
public string PersonType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Search characters used to find items
|
||||
/// </summary>
|
||||
/// <value>The index by.</value>
|
||||
public string SearchTerm { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The dynamic, localized index function name
|
||||
/// </summary>
|
||||
/// <value>The index by.</value>
|
||||
public string IndexBy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The dynamic, localized sort function name
|
||||
/// </summary>
|
||||
/// <value>The dynamic sort by.</value>
|
||||
public string DynamicSortBy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// What to sort the results by
|
||||
/// </summary>
|
||||
/// <value>The sort by.</value>
|
||||
public string SortBy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The sort order to return results with
|
||||
/// </summary>
|
||||
/// <value>The sort order.</value>
|
||||
public string SortOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Filters to apply to the results
|
||||
/// </summary>
|
||||
/// <value>The filters.</value>
|
||||
public string Filters { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fields to return within the items, in addition to basic information
|
||||
/// </summary>
|
||||
/// <value>The fields.</value>
|
||||
public string Fields { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Limit results to items containing specific genres
|
||||
/// </summary>
|
||||
/// <value>The genres.</value>
|
||||
public string Genres { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Limit results to items containing specific studios
|
||||
/// </summary>
|
||||
/// <value>The studios.</value>
|
||||
public string Studios { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the exclude item types.
|
||||
/// </summary>
|
||||
/// <value>The exclude item types.</value>
|
||||
public string ExcludeItemTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the include item types.
|
||||
/// </summary>
|
||||
/// <value>The include item types.</value>
|
||||
public string IncludeItemTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Limit results to items containing specific years
|
||||
/// </summary>
|
||||
/// <value>The years.</value>
|
||||
public string Years { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the image types.
|
||||
/// </summary>
|
||||
/// <value>The image types.</value>
|
||||
public string ImageTypes { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class ItemsService
|
||||
/// </summary>
|
||||
[Export(typeof(IRestfulService))]
|
||||
public class ItemsService : BaseRestService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetItems request)
|
||||
{
|
||||
var result = GetItems(request).Result;
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the items.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>Task{ItemsResult}.</returns>
|
||||
private async Task<ItemsResult> GetItems(GetItems request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var user = kernel.GetUserById(request.UserId);
|
||||
|
||||
var items = GetItemsToSerialize(request, user);
|
||||
|
||||
// Apply filters
|
||||
// Run them starting with the ones that are likely to reduce the list the most
|
||||
foreach (var filter in GetFilters(request).OrderByDescending(f => (int)f))
|
||||
{
|
||||
items = ApplyFilter(items, filter, user);
|
||||
}
|
||||
|
||||
items = ApplyAdditionalFilters(request, items);
|
||||
|
||||
items = ApplySearchTerm(request, items);
|
||||
|
||||
items = ApplySortOrder(request, items, user);
|
||||
|
||||
var itemsArray = items.ToArray();
|
||||
|
||||
var pagedItems = ApplyPaging(request, itemsArray);
|
||||
|
||||
var fields = GetItemFields(request).ToList();
|
||||
|
||||
var returnItems = await Task.WhenAll(pagedItems.Select(i => DtoBuilder.GetDtoBaseItem(i, user, fields))).ConfigureAwait(false);
|
||||
|
||||
return new ItemsResult
|
||||
{
|
||||
TotalRecordCount = itemsArray.Length,
|
||||
Items = returnItems
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the items to serialize.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>IEnumerable{BaseItem}.</returns>
|
||||
/// <exception cref="System.InvalidOperationException"></exception>
|
||||
private IEnumerable<BaseItem> GetItemsToSerialize(GetItems request, User user)
|
||||
{
|
||||
var item = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.ParentId, user.Id);
|
||||
|
||||
// Default list type = children
|
||||
|
||||
if (request.Recursive)
|
||||
{
|
||||
return ((Folder)item).GetRecursiveChildren(user);
|
||||
}
|
||||
|
||||
return ((Folder)item).GetChildren(user, request.IndexBy, request.DynamicSortBy, GetSortOrder(request));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies sort order
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>IEnumerable{BaseItem}.</returns>
|
||||
private IEnumerable<BaseItem> ApplySortOrder(GetItems request, IEnumerable<BaseItem> items, User user)
|
||||
{
|
||||
var isFirst = true;
|
||||
var descending = (GetSortOrder(request) ?? SortOrder.Ascending) == SortOrder.Descending;
|
||||
|
||||
IOrderedEnumerable<BaseItem> orderedItems = null;
|
||||
|
||||
foreach (var orderBy in GetOrderBy(request).Select(o => GetComparer(o, user)))
|
||||
{
|
||||
if (isFirst)
|
||||
{
|
||||
orderedItems = descending ? items.OrderByDescending(i => i, orderBy) : items.OrderBy(i => i, orderBy);
|
||||
}
|
||||
else
|
||||
{
|
||||
orderedItems = descending ? orderedItems.ThenByDescending(i => i, orderBy) : orderedItems.ThenBy(i => i, orderBy);
|
||||
}
|
||||
|
||||
isFirst = false;
|
||||
}
|
||||
|
||||
return orderedItems ?? items;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the comparer.
|
||||
/// </summary>
|
||||
/// <param name="sortBy">The sort by.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>IComparer{BaseItem}.</returns>
|
||||
/// <exception cref="System.ArgumentException"></exception>
|
||||
private IComparer<BaseItem> GetComparer(ItemSortBy sortBy, User user)
|
||||
{
|
||||
switch (sortBy)
|
||||
{
|
||||
case ItemSortBy.Album:
|
||||
return new AlbumComparer();
|
||||
case ItemSortBy.AlbumArtist:
|
||||
return new AlbumArtistComparer();
|
||||
case ItemSortBy.Artist:
|
||||
return new ArtistComparer();
|
||||
case ItemSortBy.Random:
|
||||
return new RandomComparer();
|
||||
case ItemSortBy.DateCreated:
|
||||
return new DateCreatedComparer();
|
||||
case ItemSortBy.SortName:
|
||||
return new SortNameComparer();
|
||||
case ItemSortBy.PremiereDate:
|
||||
return new PremiereDateComparer();
|
||||
case ItemSortBy.DatePlayed:
|
||||
return new DatePlayedComparer { User = user };
|
||||
default:
|
||||
throw new ArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies filtering
|
||||
/// </summary>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="filter">The filter.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>IEnumerable{BaseItem}.</returns>
|
||||
private IEnumerable<BaseItem> ApplyFilter(IEnumerable<BaseItem> items, ItemFilter filter, User user)
|
||||
{
|
||||
switch (filter)
|
||||
{
|
||||
case ItemFilter.IsFavorite:
|
||||
return items.Where(item =>
|
||||
{
|
||||
var userdata = item.GetUserData(user, false);
|
||||
|
||||
return userdata != null && userdata.IsFavorite;
|
||||
});
|
||||
|
||||
case ItemFilter.IsRecentlyAdded:
|
||||
return items.Where(item => item.IsRecentlyAdded(user));
|
||||
|
||||
case ItemFilter.IsResumable:
|
||||
return items.Where(item =>
|
||||
{
|
||||
var userdata = item.GetUserData(user, false);
|
||||
|
||||
return userdata != null && userdata.PlaybackPositionTicks > 0;
|
||||
});
|
||||
|
||||
case ItemFilter.IsPlayed:
|
||||
return items.Where(item =>
|
||||
{
|
||||
var userdata = item.GetUserData(user, false);
|
||||
|
||||
return userdata != null && userdata.PlayCount > 0;
|
||||
});
|
||||
|
||||
case ItemFilter.IsRecentlyPlayed:
|
||||
return items.Where(item => item.IsRecentlyPlayed(user));
|
||||
|
||||
case ItemFilter.IsUnplayed:
|
||||
return items.Where(item =>
|
||||
{
|
||||
var userdata = item.GetUserData(user, false);
|
||||
|
||||
return userdata == null || userdata.PlayCount == 0;
|
||||
});
|
||||
|
||||
case ItemFilter.IsFolder:
|
||||
return items.Where(item => item.IsFolder);
|
||||
|
||||
case ItemFilter.IsNotFolder:
|
||||
return items.Where(item => !item.IsFolder);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the additional filters.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <returns>IEnumerable{BaseItem}.</returns>
|
||||
private IEnumerable<BaseItem> ApplyAdditionalFilters(GetItems request, IEnumerable<BaseItem> items)
|
||||
{
|
||||
var imageTypes = GetImageTypes(request).ToArray();
|
||||
if (imageTypes.Length > 0)
|
||||
{
|
||||
items = items.Where(item => imageTypes.Any(imageType => HasImage(item, imageType)));
|
||||
}
|
||||
|
||||
// Exclude item types
|
||||
var excludeItemTypes = request.ExcludeItemTypes;
|
||||
if (!string.IsNullOrEmpty(excludeItemTypes))
|
||||
{
|
||||
var vals = excludeItemTypes.Split(',');
|
||||
items = items.Where(f => !vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
var includeItemTypes = request.IncludeItemTypes;
|
||||
if (!string.IsNullOrEmpty(includeItemTypes))
|
||||
{
|
||||
var vals = includeItemTypes.Split(',');
|
||||
items = items.Where(f => vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
var genres = request.Genres;
|
||||
|
||||
// Apply genre filter
|
||||
if (!string.IsNullOrEmpty(genres))
|
||||
{
|
||||
var vals = genres.Split(',');
|
||||
items = items.Where(f => f.Genres != null && vals.Any(v => f.Genres.Contains(v, StringComparer.OrdinalIgnoreCase)));
|
||||
}
|
||||
|
||||
var studios = request.Studios;
|
||||
|
||||
// Apply studio filter
|
||||
if (!string.IsNullOrEmpty(studios))
|
||||
{
|
||||
var vals = studios.Split(',');
|
||||
items = items.Where(f => f.Studios != null && vals.Any(v => f.Studios.Contains(v, StringComparer.OrdinalIgnoreCase)));
|
||||
}
|
||||
|
||||
var years = request.Years;
|
||||
|
||||
// Apply year filter
|
||||
if (!string.IsNullOrEmpty(years))
|
||||
{
|
||||
var vals = years.Split(',').Select(int.Parse);
|
||||
items = items.Where(f => f.ProductionYear.HasValue && vals.Contains(f.ProductionYear.Value));
|
||||
}
|
||||
|
||||
var personName = request.Person;
|
||||
|
||||
// Apply person filter
|
||||
if (!string.IsNullOrEmpty(personName))
|
||||
{
|
||||
var personType = request.PersonType;
|
||||
|
||||
items = !string.IsNullOrEmpty(personType)
|
||||
? items.Where(item => item.People != null && item.People.Any(p => p.Name.Equals(personName, StringComparison.OrdinalIgnoreCase) && p.Type.Equals(personType, StringComparison.OrdinalIgnoreCase)))
|
||||
: items.Where(item => item.People != null && item.People.Any(p => p.Name.Equals(personName, StringComparison.OrdinalIgnoreCase)));
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified item has image.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="imageType">Type of the image.</param>
|
||||
/// <returns><c>true</c> if the specified item has image; otherwise, <c>false</c>.</returns>
|
||||
private bool HasImage(BaseItem item, ImageType imageType)
|
||||
{
|
||||
if (imageType == ImageType.Backdrop)
|
||||
{
|
||||
return item.BackdropImagePaths != null && item.BackdropImagePaths.Count > 0;
|
||||
}
|
||||
|
||||
if (imageType == ImageType.Screenshot)
|
||||
{
|
||||
return item.ScreenshotImagePaths != null && item.ScreenshotImagePaths.Count > 0;
|
||||
}
|
||||
|
||||
if (imageType == ImageType.ChapterImage)
|
||||
{
|
||||
var video = item as Video;
|
||||
|
||||
if (video != null)
|
||||
{
|
||||
return video.Chapters != null && video.Chapters.Any(c => !string.IsNullOrEmpty(c.ImagePath));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return item.HasImage(imageType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the search term.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <returns>IEnumerable{BaseItem}.</returns>
|
||||
private IEnumerable<BaseItem> ApplySearchTerm(GetItems request, IEnumerable<BaseItem> items)
|
||||
{
|
||||
var term = request.SearchTerm;
|
||||
|
||||
if (!string.IsNullOrEmpty(term))
|
||||
{
|
||||
items = items.Where(i => i.Name.StartsWith(term, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the paging.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <returns>IEnumerable{BaseItem}.</returns>
|
||||
private IEnumerable<BaseItem> ApplyPaging(GetItems request, IEnumerable<BaseItem> items)
|
||||
{
|
||||
// Start at
|
||||
if (request.StartIndex.HasValue)
|
||||
{
|
||||
items = items.Skip(request.StartIndex.Value);
|
||||
}
|
||||
|
||||
// Return limit
|
||||
if (request.Limit.HasValue)
|
||||
{
|
||||
items = items.Take(request.Limit.Value);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sort order.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Nullable{SortOrder}.</returns>
|
||||
private SortOrder? GetSortOrder(GetItems request)
|
||||
{
|
||||
if (string.IsNullOrEmpty(request.SortOrder))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return (SortOrder)Enum.Parse(typeof(SortOrder), request.SortOrder, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the filters.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>IEnumerable{ItemFilter}.</returns>
|
||||
private IEnumerable<ItemFilter> GetFilters(GetItems request)
|
||||
{
|
||||
var val = request.Filters;
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return new ItemFilter[] { };
|
||||
}
|
||||
|
||||
return val.Split(',').Select(v => (ItemFilter)Enum.Parse(typeof(ItemFilter), v, true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item fields.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>IEnumerable{ItemFields}.</returns>
|
||||
private IEnumerable<ItemFields> GetItemFields(GetItems request)
|
||||
{
|
||||
var val = request.Fields;
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return new ItemFields[] { };
|
||||
}
|
||||
|
||||
return val.Split(',').Select(v => (ItemFields)Enum.Parse(typeof(ItemFields), v, true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the order by.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>IEnumerable{ItemSortBy}.</returns>
|
||||
private IEnumerable<ItemSortBy> GetOrderBy(GetItems request)
|
||||
{
|
||||
var val = request.SortBy;
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return new ItemSortBy[] { };
|
||||
}
|
||||
|
||||
return val.Split(',').Select(v => (ItemSortBy)Enum.Parse(typeof(ItemSortBy), v, true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the image types.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>IEnumerable{ImageType}.</returns>
|
||||
private IEnumerable<ImageType> GetImageTypes(GetItems request)
|
||||
{
|
||||
var val = request.ImageTypes;
|
||||
|
||||
if (string.IsNullOrEmpty(val))
|
||||
{
|
||||
return new ImageType[] { };
|
||||
}
|
||||
|
||||
return val.Split(',').Select(v => (ImageType)Enum.Parse(typeof(ImageType), v, true));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class DateCreatedComparer
|
||||
/// </summary>
|
||||
public class DateCreatedComparer : IComparer<BaseItem>
|
||||
{
|
||||
/// <summary>
|
||||
/// Compares the specified x.
|
||||
/// </summary>
|
||||
/// <param name="x">The x.</param>
|
||||
/// <param name="y">The y.</param>
|
||||
/// <returns>System.Int32.</returns>
|
||||
public int Compare(BaseItem x, BaseItem y)
|
||||
{
|
||||
return x.DateCreated.CompareTo(y.DateCreated);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class RandomComparer
|
||||
/// </summary>
|
||||
public class RandomComparer : IComparer<BaseItem>
|
||||
{
|
||||
/// <summary>
|
||||
/// Compares the specified x.
|
||||
/// </summary>
|
||||
/// <param name="x">The x.</param>
|
||||
/// <param name="y">The y.</param>
|
||||
/// <returns>System.Int32.</returns>
|
||||
public int Compare(BaseItem x, BaseItem y)
|
||||
{
|
||||
return Guid.NewGuid().CompareTo(Guid.NewGuid());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class SortNameComparer
|
||||
/// </summary>
|
||||
public class SortNameComparer : IComparer<BaseItem>
|
||||
{
|
||||
/// <summary>
|
||||
/// Compares the specified x.
|
||||
/// </summary>
|
||||
/// <param name="x">The x.</param>
|
||||
/// <param name="y">The y.</param>
|
||||
/// <returns>System.Int32.</returns>
|
||||
public int Compare(BaseItem x, BaseItem y)
|
||||
{
|
||||
return string.Compare(x.SortName, y.SortName, StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class AlbumArtistComparer
|
||||
/// </summary>
|
||||
public class AlbumArtistComparer : IComparer<BaseItem>
|
||||
{
|
||||
/// <summary>
|
||||
/// Compares the specified x.
|
||||
/// </summary>
|
||||
/// <param name="x">The x.</param>
|
||||
/// <param name="y">The y.</param>
|
||||
/// <returns>System.Int32.</returns>
|
||||
public int Compare(BaseItem x, BaseItem y)
|
||||
{
|
||||
return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value.
|
||||
/// </summary>
|
||||
/// <param name="x">The x.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
private string GetValue(BaseItem x)
|
||||
{
|
||||
var audio = x as Audio;
|
||||
|
||||
return audio == null ? string.Empty : audio.AlbumArtist;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class AlbumComparer
|
||||
/// </summary>
|
||||
public class AlbumComparer : IComparer<BaseItem>
|
||||
{
|
||||
/// <summary>
|
||||
/// Compares the specified x.
|
||||
/// </summary>
|
||||
/// <param name="x">The x.</param>
|
||||
/// <param name="y">The y.</param>
|
||||
/// <returns>System.Int32.</returns>
|
||||
public int Compare(BaseItem x, BaseItem y)
|
||||
{
|
||||
return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value.
|
||||
/// </summary>
|
||||
/// <param name="x">The x.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
private string GetValue(BaseItem x)
|
||||
{
|
||||
var audio = x as Audio;
|
||||
|
||||
return audio == null ? string.Empty : audio.Album;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class ArtistComparer
|
||||
/// </summary>
|
||||
public class ArtistComparer : IComparer<BaseItem>
|
||||
{
|
||||
/// <summary>
|
||||
/// Compares the specified x.
|
||||
/// </summary>
|
||||
/// <param name="x">The x.</param>
|
||||
/// <param name="y">The y.</param>
|
||||
/// <returns>System.Int32.</returns>
|
||||
public int Compare(BaseItem x, BaseItem y)
|
||||
{
|
||||
return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value.
|
||||
/// </summary>
|
||||
/// <param name="x">The x.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
private string GetValue(BaseItem x)
|
||||
{
|
||||
var audio = x as Audio;
|
||||
|
||||
return audio == null ? string.Empty : audio.Artist;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class PremiereDateComparer
|
||||
/// </summary>
|
||||
public class PremiereDateComparer : IComparer<BaseItem>
|
||||
{
|
||||
/// <summary>
|
||||
/// Compares the specified x.
|
||||
/// </summary>
|
||||
/// <param name="x">The x.</param>
|
||||
/// <param name="y">The y.</param>
|
||||
/// <returns>System.Int32.</returns>
|
||||
public int Compare(BaseItem x, BaseItem y)
|
||||
{
|
||||
return GetDate(x).CompareTo(GetDate(y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the date.
|
||||
/// </summary>
|
||||
/// <param name="x">The x.</param>
|
||||
/// <returns>DateTime.</returns>
|
||||
private DateTime GetDate(BaseItem x)
|
||||
{
|
||||
if (x.PremiereDate.HasValue)
|
||||
{
|
||||
return x.PremiereDate.Value;
|
||||
}
|
||||
|
||||
if (x.ProductionYear.HasValue)
|
||||
{
|
||||
return new DateTime(x.ProductionYear.Value, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
}
|
||||
return DateTime.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class DatePlayedComparer
|
||||
/// </summary>
|
||||
public class DatePlayedComparer : IComparer<BaseItem>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user.
|
||||
/// </summary>
|
||||
/// <value>The user.</value>
|
||||
public User User { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Compares the specified x.
|
||||
/// </summary>
|
||||
/// <param name="x">The x.</param>
|
||||
/// <param name="y">The y.</param>
|
||||
/// <returns>System.Int32.</returns>
|
||||
public int Compare(BaseItem x, BaseItem y)
|
||||
{
|
||||
return GetDate(x).CompareTo(GetDate(y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the date.
|
||||
/// </summary>
|
||||
/// <param name="x">The x.</param>
|
||||
/// <returns>DateTime.</returns>
|
||||
private DateTime GetDate(BaseItem x)
|
||||
{
|
||||
var userdata = x.GetUserData(User, false);
|
||||
|
||||
if (userdata != null && userdata.LastPlayedDate.HasValue)
|
||||
{
|
||||
return userdata.LastPlayedDate.Value;
|
||||
}
|
||||
|
||||
return DateTime.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
103
MediaBrowser.Api/UserLibrary/PersonsService.cs
Normal file
103
MediaBrowser.Api/UserLibrary/PersonsService.cs
Normal file
@ -0,0 +1,103 @@
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using ServiceStack.ServiceHost;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.UserLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetPersons
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/Items/{Id}/Persons", "GET")]
|
||||
[Route("/Users/{UserId}/Items/Root/Persons", "GET")]
|
||||
public class GetPersons : GetItemsByName
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the person types.
|
||||
/// </summary>
|
||||
/// <value>The person types.</value>
|
||||
public string PersonTypes { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class PersonsService
|
||||
/// </summary>
|
||||
[Export(typeof(IRestfulService))]
|
||||
public class PersonsService : BaseItemsByNameService<Person>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetPersons request)
|
||||
{
|
||||
var result = GetResult(request).Result;
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all items.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
|
||||
protected override IEnumerable<Tuple<string, Func<int>>> GetAllItems(GetItemsByName request, IEnumerable<BaseItem> items, User user)
|
||||
{
|
||||
var inputPersonTypes = ((GetPersons) request).PersonTypes;
|
||||
var personTypes = string.IsNullOrEmpty(inputPersonTypes) ? new string[] { } : inputPersonTypes.Split(',');
|
||||
|
||||
var itemsList = items.Where(i => i.People != null).ToList();
|
||||
|
||||
// Either get all people, or all people filtered by a specific person type
|
||||
var allPeople = GetAllPeople(itemsList, personTypes);
|
||||
|
||||
return allPeople
|
||||
.Select(i => i.Name)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
|
||||
.Select(name => new Tuple<string, Func<int>>(name, () =>
|
||||
{
|
||||
if (personTypes.Length == 0)
|
||||
{
|
||||
return itemsList.Count(i => i.People.Any(p => p.Name.Equals(name, StringComparison.OrdinalIgnoreCase)));
|
||||
}
|
||||
|
||||
return itemsList.Count(i => i.People.Any(p => p.Name.Equals(name, StringComparison.OrdinalIgnoreCase) && personTypes.Contains(p.Type ?? string.Empty, StringComparer.OrdinalIgnoreCase)));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all people.
|
||||
/// </summary>
|
||||
/// <param name="itemsList">The items list.</param>
|
||||
/// <param name="personTypes">The person types.</param>
|
||||
/// <returns>IEnumerable{PersonInfo}.</returns>
|
||||
private IEnumerable<PersonInfo> GetAllPeople(IEnumerable<BaseItem> itemsList, string[] personTypes)
|
||||
{
|
||||
return personTypes.Length == 0 ?
|
||||
itemsList.SelectMany(i => i.People) :
|
||||
itemsList.SelectMany(i => i.People.Where(p => personTypes.Contains(p.Type ?? string.Empty, StringComparer.OrdinalIgnoreCase)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the entity.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <returns>Task{Genre}.</returns>
|
||||
protected override Task<Person> GetEntity(string name)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
return kernel.LibraryManager.GetPerson(name);
|
||||
}
|
||||
}
|
||||
}
|
69
MediaBrowser.Api/UserLibrary/StudiosService.cs
Normal file
69
MediaBrowser.Api/UserLibrary/StudiosService.cs
Normal file
@ -0,0 +1,69 @@
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using ServiceStack.ServiceHost;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.UserLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetStudios
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/Items/{Id}/Studios", "GET")]
|
||||
[Route("/Users/{UserId}/Items/Root/Studios", "GET")]
|
||||
public class GetStudios : GetItemsByName
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class StudiosService
|
||||
/// </summary>
|
||||
[Export(typeof(IRestfulService))]
|
||||
public class StudiosService : BaseItemsByNameService<Studio>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetStudios request)
|
||||
{
|
||||
var result = GetResult(request).Result;
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all items.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
|
||||
protected override IEnumerable<Tuple<string, Func<int>>> GetAllItems(GetItemsByName request, IEnumerable<BaseItem> items, User user)
|
||||
{
|
||||
var itemsList = items.Where(i => i.Studios != null).ToList();
|
||||
|
||||
return itemsList
|
||||
.SelectMany(i => i.Studios)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.Select(name => new Tuple<string, Func<int>>(name, () => itemsList.Count(i => i.Studios.Contains(name, StringComparer.OrdinalIgnoreCase))));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the entity.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <returns>Task{Studio}.</returns>
|
||||
protected override Task<Studio> GetEntity(string name)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
return kernel.LibraryManager.GetStudio(name);
|
||||
}
|
||||
}
|
||||
}
|
506
MediaBrowser.Api/UserLibrary/UserLibraryService.cs
Normal file
506
MediaBrowser.Api/UserLibrary/UserLibraryService.cs
Normal file
@ -0,0 +1,506 @@
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Serialization;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using ServiceStack.ServiceHost;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using ServiceStack.Text.Controller;
|
||||
|
||||
namespace MediaBrowser.Api.UserLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetItem
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/Items/{Id}", "GET")]
|
||||
[Route("/Users/{UserId}/Items/Root", "GET")]
|
||||
public class GetItem : IReturn<DtoBaseItem>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetIntros
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/Items/{Id}/Intros", "GET")]
|
||||
[ServiceStack.ServiceHost.Api(("Gets intros to play before the main media item plays"))]
|
||||
public class GetIntros : IReturn<List<string>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the item id.
|
||||
/// </summary>
|
||||
/// <value>The item id.</value>
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class UpdateDisplayPreferences
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/Items/{Id}/DisplayPreferences", "GET")]
|
||||
[ServiceStack.ServiceHost.Api(("Updates a user's display preferences for an item"))]
|
||||
public class UpdateDisplayPreferences : IReturnVoid, IRequiresRequestStream
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The raw Http Request Input Stream
|
||||
/// </summary>
|
||||
/// <value>The request stream.</value>
|
||||
public Stream RequestStream { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetVirtualFolders
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/VirtualFolders", "GET")]
|
||||
public class GetVirtualFolders : IReturn<List<VirtualFolderInfo>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
public Guid UserId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class MarkFavoriteItem
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/FavoriteItems/{Id}", "POST")]
|
||||
public class MarkFavoriteItem : IReturnVoid
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class UnmarkFavoriteItem
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/FavoriteItems/{Id}", "DELETE")]
|
||||
public class UnmarkFavoriteItem : IReturnVoid
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class ClearUserItemRating
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/Items/{Id}/Rating", "DELETE")]
|
||||
public class DeleteUserItemRating : IReturnVoid
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class UpdateUserItemRating
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/Items/{Id}/Rating", "POST")]
|
||||
public class UpdateUserItemRating : IReturnVoid
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this <see cref="UpdateUserItemRating" /> is likes.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if likes; otherwise, <c>false</c>.</value>
|
||||
public bool Likes { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class MarkPlayedItem
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/PlayedItems/{Id}", "POST")]
|
||||
public class MarkPlayedItem : IReturnVoid
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class MarkUnplayedItem
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/PlayedItems/{Id}", "DELETE")]
|
||||
public class MarkUnplayedItem : IReturnVoid
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Users/{UserId}/Items/{Id}/LocalTrailers", "GET")]
|
||||
public class GetLocalTrailers : IReturn<List<DtoBaseItem>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Users/{UserId}/Items/{Id}/SpecialFeatures", "GET")]
|
||||
public class GetSpecialFeatures : IReturn<List<DtoBaseItem>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Class UserLibraryService
|
||||
/// </summary>
|
||||
[Export(typeof(IRestfulService))]
|
||||
public class UserLibraryService : BaseRestService
|
||||
{
|
||||
public object Get(GetSpecialFeatures request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var user = kernel.GetUserById(request.UserId);
|
||||
|
||||
var item = DtoBuilder.GetItemByClientId(request.Id, user.Id);
|
||||
|
||||
// Get everything
|
||||
var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList();
|
||||
|
||||
var movie = (Movie)item;
|
||||
|
||||
var items = movie.SpecialFeatures.Select(i => DtoBuilder.GetDtoBaseItem(item, user, fields)).AsParallel().Select(t => t.Result).ToList();
|
||||
|
||||
return ToOptimizedResult(items);
|
||||
}
|
||||
|
||||
public object Get(GetLocalTrailers request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var user = kernel.GetUserById(request.UserId);
|
||||
|
||||
var item = DtoBuilder.GetItemByClientId(request.Id, user.Id);
|
||||
|
||||
// Get everything
|
||||
var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList();
|
||||
|
||||
var items = item.LocalTrailers.Select(i => DtoBuilder.GetDtoBaseItem(item, user, fields)).AsParallel().Select(t => t.Result).ToList();
|
||||
|
||||
return ToOptimizedResult(items);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetItem request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var user = kernel.GetUserById(request.UserId);
|
||||
|
||||
var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.Id, user.Id);
|
||||
|
||||
// Get everything
|
||||
var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList();
|
||||
|
||||
var result = DtoBuilder.GetDtoBaseItem(item, user, fields).Result;
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetVirtualFolders request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var user = kernel.GetUserById(request.UserId);
|
||||
|
||||
var result = kernel.LibraryManager.GetVirtualFolders(user).ToList();
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetIntros request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var user = kernel.GetUserById(request.UserId);
|
||||
|
||||
var item = DtoBuilder.GetItemByClientId(request.Id, user.Id);
|
||||
|
||||
var result = kernel.IntroProviders.SelectMany(i => i.GetIntros(item, user));
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public void Post(UpdateDisplayPreferences request)
|
||||
{
|
||||
// We need to parse this manually because we told service stack not to with IRequiresRequestStream
|
||||
// https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs
|
||||
var pathInfo = PathInfo.Parse(Request.PathInfo);
|
||||
var userId = new Guid(pathInfo.GetArgumentValue<string>(1));
|
||||
var itemId = pathInfo.GetArgumentValue<string>(3);
|
||||
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var user = kernel.GetUserById(userId);
|
||||
|
||||
var item = (Folder)DtoBuilder.GetItemByClientId(itemId, user.Id);
|
||||
|
||||
var displayPreferences = JsonSerializer.DeserializeFromStream<DisplayPreferences>(request.RequestStream);
|
||||
|
||||
var task = kernel.LibraryManager.SaveDisplayPreferencesForFolder(user, item, displayPreferences);
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public void Post(MarkFavoriteItem request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var user = kernel.GetUserById(request.UserId);
|
||||
|
||||
var item = (Folder)DtoBuilder.GetItemByClientId(request.Id, user.Id);
|
||||
|
||||
// Get the user data for this item
|
||||
var data = item.GetUserData(user, true);
|
||||
|
||||
// Set favorite status
|
||||
data.IsFavorite = true;
|
||||
|
||||
var task = kernel.UserDataManager.SaveUserDataForItem(user, item, data);
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public void Delete(UnmarkFavoriteItem request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var user = kernel.GetUserById(request.UserId);
|
||||
|
||||
var item = (Folder)DtoBuilder.GetItemByClientId(request.Id, user.Id);
|
||||
|
||||
// Get the user data for this item
|
||||
var data = item.GetUserData(user, true);
|
||||
|
||||
// Set favorite status
|
||||
data.IsFavorite = false;
|
||||
|
||||
var task = kernel.UserDataManager.SaveUserDataForItem(user, item, data);
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public void Delete(DeleteUserItemRating request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var user = kernel.GetUserById(request.UserId);
|
||||
|
||||
var item = (Folder)DtoBuilder.GetItemByClientId(request.Id, user.Id);
|
||||
|
||||
// Get the user data for this item
|
||||
var data = item.GetUserData(user, true);
|
||||
|
||||
data.Rating = null;
|
||||
|
||||
var task = kernel.UserDataManager.SaveUserDataForItem(user, item, data);
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public void Post(UpdateUserItemRating request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var user = kernel.GetUserById(request.UserId);
|
||||
|
||||
var item = (Folder)DtoBuilder.GetItemByClientId(request.Id, user.Id);
|
||||
|
||||
// Get the user data for this item
|
||||
var data = item.GetUserData(user, true);
|
||||
|
||||
data.Likes = request.Likes;
|
||||
|
||||
var task = kernel.UserDataManager.SaveUserDataForItem(user, item, data);
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public void Post(MarkPlayedItem request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var user = kernel.GetUserById(request.UserId);
|
||||
|
||||
var task = UpdatePlayedStatus(user, request.Id, true);
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public void Delete(MarkUnplayedItem request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var user = kernel.GetUserById(request.UserId);
|
||||
|
||||
var task = UpdatePlayedStatus(user, request.Id, false);
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the played status.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="itemId">The item id.</param>
|
||||
/// <param name="wasPlayed">if set to <c>true</c> [was played].</param>
|
||||
/// <returns>Task.</returns>
|
||||
private Task UpdatePlayedStatus(User user, string itemId, bool wasPlayed)
|
||||
{
|
||||
var item = DtoBuilder.GetItemByClientId(itemId, user.Id);
|
||||
|
||||
return item.SetPlayedStatus(user, wasPlayed);
|
||||
}
|
||||
}
|
||||
}
|
75
MediaBrowser.Api/UserLibrary/YearsService.cs
Normal file
75
MediaBrowser.Api/UserLibrary/YearsService.cs
Normal file
@ -0,0 +1,75 @@
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using ServiceStack.ServiceHost;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.UserLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetYears
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/Items/{Id}/Years", "GET")]
|
||||
[Route("/Users/{UserId}/Items/Root/Years", "GET")]
|
||||
public class GetYears : GetItemsByName
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class YearsService
|
||||
/// </summary>
|
||||
[Export(typeof(IRestfulService))]
|
||||
public class YearsService : BaseItemsByNameService<Year>
|
||||
{
|
||||
/// <summary>
|
||||
/// The us culture
|
||||
/// </summary>
|
||||
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetYears request)
|
||||
{
|
||||
var result = GetResult(request).Result;
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all items.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
|
||||
protected override IEnumerable<Tuple<string, Func<int>>> GetAllItems(GetItemsByName request, IEnumerable<BaseItem> items, User user)
|
||||
{
|
||||
var itemsList = items.Where(i => i.ProductionYear != null).ToList();
|
||||
|
||||
return itemsList
|
||||
.Select(i => i.ProductionYear.Value)
|
||||
.Distinct()
|
||||
.Select(year => new Tuple<string, Func<int>>(year.ToString(UsCulture), () => itemsList.Count(i => i.ProductionYear.HasValue && i.ProductionYear.Value == year)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the entity.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <returns>Task{Studio}.</returns>
|
||||
protected override Task<Year> GetEntity(string name)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
return kernel.LibraryManager.GetYear(int.Parse(name, UsCulture));
|
||||
}
|
||||
}
|
||||
}
|
297
MediaBrowser.Api/UserService.cs
Normal file
297
MediaBrowser.Api/UserService.cs
Normal file
@ -0,0 +1,297 @@
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Serialization;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.DTO;
|
||||
using ServiceStack.ServiceHost;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using ServiceStack.Text.Controller;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetUsers
|
||||
/// </summary>
|
||||
[Route("/Users", "GET")]
|
||||
public class GetUsers : IReturn<List<DtoUser>>
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetUser
|
||||
/// </summary>
|
||||
[Route("/Users/{Id}", "GET")]
|
||||
public class GetUser : IReturn<DtoUser>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class DeleteUser
|
||||
/// </summary>
|
||||
[Route("/Users/{Id}", "DELETE")]
|
||||
public class DeleteUser : IReturnVoid
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class AuthenticateUser
|
||||
/// </summary>
|
||||
[Route("/Users/{Id}/Authenticate", "POST")]
|
||||
public class AuthenticateUser : IReturnVoid
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the password.
|
||||
/// </summary>
|
||||
/// <value>The password.</value>
|
||||
public string Password { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class UpdateUserPassword
|
||||
/// </summary>
|
||||
[Route("/Users/{Id}/Password", "POST")]
|
||||
public class UpdateUserPassword : IReturnVoid
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the password.
|
||||
/// </summary>
|
||||
/// <value>The password.</value>
|
||||
public string CurrentPassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the new password.
|
||||
/// </summary>
|
||||
/// <value>The new password.</value>
|
||||
public string NewPassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether [reset password].
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if [reset password]; otherwise, <c>false</c>.</value>
|
||||
public bool ResetPassword { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class UpdateUser
|
||||
/// </summary>
|
||||
[Route("/Users/{Id}", "POST")]
|
||||
public class UpdateUser : IRequiresRequestStream, IReturnVoid
|
||||
{
|
||||
/// <summary>
|
||||
/// The raw Http Request Input Stream
|
||||
/// </summary>
|
||||
/// <value>The request stream.</value>
|
||||
public Stream RequestStream { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class CreateUser
|
||||
/// </summary>
|
||||
[Route("/Users", "POST")]
|
||||
public class CreateUser : IRequiresRequestStream, IReturn<DtoUser>
|
||||
{
|
||||
/// <summary>
|
||||
/// The raw Http Request Input Stream
|
||||
/// </summary>
|
||||
/// <value>The request stream.</value>
|
||||
public Stream RequestStream { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class UsersService
|
||||
/// </summary>
|
||||
[Export(typeof(IRestfulService))]
|
||||
public class UserService : BaseRestService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetUsers request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var result = kernel.Users.OrderBy(u => u.Name).Select(DtoBuilder.GetDtoUser).ToList();
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetUser request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var user = kernel.GetUserById(request.Id);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
throw new ResourceNotFoundException("User not found");
|
||||
}
|
||||
|
||||
var result = DtoBuilder.GetDtoUser(user);
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public void Delete(DeleteUser request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var user = kernel.GetUserById(request.Id);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
throw new ResourceNotFoundException("User not found");
|
||||
}
|
||||
|
||||
var task = kernel.UserManager.DeleteUser(user);
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public void Post(AuthenticateUser request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var user = kernel.GetUserById(request.Id);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
throw new ResourceNotFoundException("User not found");
|
||||
}
|
||||
|
||||
var success = kernel.UserManager.AuthenticateUser(user, request.Password).Result;
|
||||
|
||||
if (!success)
|
||||
{
|
||||
// Unauthorized
|
||||
throw new ResourceNotFoundException("Invalid user or password entered.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public void Post(UpdateUserPassword request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var user = kernel.GetUserById(request.Id);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
throw new ResourceNotFoundException("User not found");
|
||||
}
|
||||
|
||||
if (request.ResetPassword)
|
||||
{
|
||||
var task = user.ResetPassword();
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
else
|
||||
{
|
||||
var success = kernel.UserManager.AuthenticateUser(user, request.CurrentPassword).Result;
|
||||
|
||||
if (!success)
|
||||
{
|
||||
throw new ResourceNotFoundException("Invalid user or password entered.");
|
||||
}
|
||||
|
||||
var task = user.ChangePassword(request.NewPassword);
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public void Post(UpdateUser request)
|
||||
{
|
||||
// We need to parse this manually because we told service stack not to with IRequiresRequestStream
|
||||
// https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs
|
||||
var pathInfo = PathInfo.Parse(Request.PathInfo);
|
||||
var id = new Guid(pathInfo.GetArgumentValue<string>(1));
|
||||
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var dtoUser = JsonSerializer.DeserializeFromStream<DtoUser>(request.RequestStream);
|
||||
|
||||
var user = kernel.GetUserById(id);
|
||||
|
||||
var task = user.Name.Equals(dtoUser.Name, StringComparison.Ordinal) ? kernel.UserManager.UpdateUser(user) : kernel.UserManager.RenameUser(user, dtoUser.Name);
|
||||
|
||||
Task.WaitAll(task);
|
||||
|
||||
user.UpdateConfiguration(dtoUser.Configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Post(CreateUser request)
|
||||
{
|
||||
var kernel = (Kernel)Kernel;
|
||||
|
||||
var dtoUser = JsonSerializer.DeserializeFromStream<DtoUser>(request.RequestStream);
|
||||
|
||||
var newUser = kernel.UserManager.CreateUser(dtoUser.Name).Result;
|
||||
|
||||
var result = DtoBuilder.GetDtoUser(newUser);
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
}
|
||||
}
|
46
MediaBrowser.Api/WeatherService.cs
Normal file
46
MediaBrowser.Api/WeatherService.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Model.Weather;
|
||||
using ServiceStack.ServiceHost;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// Class Weather
|
||||
/// </summary>
|
||||
[Route("/Weather", "GET")]
|
||||
public class GetWeather : IReturn<WeatherInfo>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the location.
|
||||
/// </summary>
|
||||
/// <value>The location.</value>
|
||||
public string Location { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class WeatherService
|
||||
/// </summary>
|
||||
[Export(typeof(IRestfulService))]
|
||||
public class WeatherService : BaseRestService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetWeather request)
|
||||
{
|
||||
var kernel = (Kernel) Kernel;
|
||||
|
||||
var location = string.IsNullOrWhiteSpace(request.Location) ? kernel.Configuration.WeatherLocation : request.Location;
|
||||
|
||||
var result = kernel.WeatherProviders.First().GetWeatherInfoAsync(location, CancellationToken.None).Result;
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
}
|
||||
}
|
876
MediaBrowser.Api/options.xml
Normal file
876
MediaBrowser.Api/options.xml
Normal file
@ -0,0 +1,876 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Options>
|
||||
<!-- ************************************************************* -->
|
||||
<!-- * DO NOT EDIT MANUALLY USE METABROWSER SETTINGS GUI TO EDIT * -->
|
||||
<!-- ************************************************************* -->
|
||||
<Locations>
|
||||
<Location enabled="True" group="">D:\Video\TV\</Location>
|
||||
<Location enabled="True" group="">D:\Video\Movies\</Location>
|
||||
<Location enabled="True" group="Music Videos">D:\Video\Music Videos\</Location>
|
||||
</Locations>
|
||||
<Window>
|
||||
<MetaBrowser Maximized="True" Width="444" Height="2224" X="240" Y="50" />
|
||||
<Options Maximized="False" Width="1120" Height="885" X="470" Y="239" />
|
||||
<AutoUpdate Maximized="False" Width="745" Height="556" X="332" Y="220" />
|
||||
<Search Maximized="False" Width="622" Height="533" X="642" Y="532" />
|
||||
<Poster Maximized="False" Width="994" Height="352" X="1150" Y="542" />
|
||||
<Backdrop Maximized="False" Width="806" Height="440" X="542" Y="332" />
|
||||
<Banner Maximized="False" Width="794" Height="410" X="915" Y="515" />
|
||||
<Episode Maximized="False" Width="798" Height="250" X="0" Y="0" />
|
||||
<Logo Maximized="False" Width="645" Height="400" X="349" Y="312" />
|
||||
<ClearArt Maximized="False" Width="806" Height="440" X="0" Y="0" />
|
||||
<Thumb Maximized="False" Width="806" Height="440" X="0" Y="0" />
|
||||
<Person Maximized="False" Width="994" Height="350" X="0" Y="0" />
|
||||
<ExportList Maximized="False" Width="802" Height="600" X="927" Y="383" />
|
||||
<ManageFilters Maximized="False" Width="860" Height="580" X="157" Y="226" />
|
||||
<DeleteMedia Maximized="False" Width="600" Height="420" X="0" Y="0" />
|
||||
<ProcessProfile Maximized="False" Width="691" Height="868" X="1605" Y="333" />
|
||||
<RealtimeUpdateFields Maximized="False" Width="350" Height="473" X="390" Y="436" />
|
||||
<RenameHistory Maximized="False" Width="1083" Height="677" X="128" Y="252" />
|
||||
<MovieConfirm Maximized="False" Width="800" Height="600" X="0" Y="0" />
|
||||
</Window>
|
||||
<IsFirstRun>False</IsFirstRun>
|
||||
<ShowExpireMessage>True</ShowExpireMessage>
|
||||
<Version>2.2.41</Version>
|
||||
<CacheFolder>C:\ProgramData\MetaBrowser 2.0\Cache\</CacheFolder>
|
||||
<EnableLogging>False</EnableLogging>
|
||||
<LogDeleteDays>10</LogDeleteDays>
|
||||
<RefreshOnStart>True</RefreshOnStart>
|
||||
<FetchOnFirstRefresh>False</FetchOnFirstRefresh>
|
||||
<FetchOnEveryRefresh>False</FetchOnEveryRefresh>
|
||||
<RealTimeMonitoring>True</RealTimeMonitoring>
|
||||
<PollLocations>False</PollLocations>
|
||||
<FetchOnDetect>True</FetchOnDetect>
|
||||
<MinimizeToTray>True</MinimizeToTray>
|
||||
<CloseToTray>False</CloseToTray>
|
||||
<LoadOnStartup>True</LoadOnStartup>
|
||||
<LoadMinimizedToTray>False</LoadMinimizedToTray>
|
||||
<ReplaceMissingOnly>False</ReplaceMissingOnly>
|
||||
<ForceUpdateImages>False</ForceUpdateImages>
|
||||
<FetchIdOnly>False</FetchIdOnly>
|
||||
<AutoSave>False</AutoSave>
|
||||
<ForceSave>False</ForceSave>
|
||||
<ForceAutoSave>False</ForceAutoSave>
|
||||
<ImageAddTypePoster>replace</ImageAddTypePoster>
|
||||
<ImageAddTypeBackdrop>add</ImageAddTypeBackdrop>
|
||||
<ImageAddTypeBanner>replace</ImageAddTypeBanner>
|
||||
<ImageAddTypeLogo>replace</ImageAddTypeLogo>
|
||||
<ImageAddTypeClearArt>replace</ImageAddTypeClearArt>
|
||||
<ImageAddTypeThumb>replace</ImageAddTypeThumb>
|
||||
<ValidVideoExtensions>.iso;.ts;.avi;.mpg;.mkv;.mp4;.mov;.wmv;.dvr-ms;.m4v;.wtv;.flv;.ogm</ValidVideoExtensions>
|
||||
<DynamicFiltering>True</DynamicFiltering>
|
||||
<IgnoreHiddenItems>True</IgnoreHiddenItems>
|
||||
<EnableMapping>True</EnableMapping>
|
||||
<ExtensionAsType>True</ExtensionAsType>
|
||||
<ACD>
|
||||
</ACD>
|
||||
<ValueLists>
|
||||
<MediaTypes>
|
||||
<Value>AVI</Value>
|
||||
<Value>Blu-ray</Value>
|
||||
<Value>DVD</Value>
|
||||
<Value>HD DVD</Value>
|
||||
<Value>MKV</Value>
|
||||
</MediaTypes>
|
||||
<Genres>
|
||||
<Value>Action</Value>
|
||||
<Value>Adventure</Value>
|
||||
<Value>Animation</Value>
|
||||
<Value>Biography</Value>
|
||||
<Value>Comedy</Value>
|
||||
<Value>Crime</Value>
|
||||
<Value>Documentary</Value>
|
||||
<Value>Drama</Value>
|
||||
<Value>Family</Value>
|
||||
<Value>Fantasy</Value>
|
||||
<Value>Film-Noir</Value>
|
||||
<Value>Game-Show</Value>
|
||||
<Value>History</Value>
|
||||
<Value>Horror</Value>
|
||||
<Value>Music</Value>
|
||||
<Value>Musical</Value>
|
||||
<Value>Mystery</Value>
|
||||
<Value>News</Value>
|
||||
<Value>Reality-TV</Value>
|
||||
<Value>Romance</Value>
|
||||
<Value>Sci-Fi</Value>
|
||||
<Value>Short</Value>
|
||||
<Value>Sport</Value>
|
||||
<Value>Talk-Show</Value>
|
||||
<Value>Thriller</Value>
|
||||
<Value>War</Value>
|
||||
<Value>Western</Value>
|
||||
</Genres>
|
||||
<AspectRatio>
|
||||
<Value>1.33:1</Value>
|
||||
<Value>1.78:1</Value>
|
||||
<Value>1.85:1</Value>
|
||||
<Value>2.35:1</Value>
|
||||
<Value>2.40:1</Value>
|
||||
</AspectRatio>
|
||||
<MovieStudios>
|
||||
<Value>20th Century Fox</Value>
|
||||
<Value>20th Century Fox Home Entertainment</Value>
|
||||
<Value>Amblin Entertainment</Value>
|
||||
<Value>Beacon Pictures</Value>
|
||||
<Value>Castle Rock</Value>
|
||||
<Value>Centropolis Entertainment</Value>
|
||||
<Value>Columbia Pictures</Value>
|
||||
<Value>Dimension Films</Value>
|
||||
<Value>Disney</Value>
|
||||
<Value>DreamWorks Pictures</Value>
|
||||
<Value>Hollywood Pictures</Value>
|
||||
<Value>Hyde Park Entertainment</Value>
|
||||
<Value>Imagine Entertainment</Value>
|
||||
<Value>Legendary Pictures</Value>
|
||||
<Value>Lions Gate</Value>
|
||||
<Value>Metro-Goldwyn-Mayer Pictures</Value>
|
||||
<Value>Metro-Goldwyn-Mayer Studios</Value>
|
||||
<Value>MGM Home Entertainment</Value>
|
||||
<Value>Millennium Films</Value>
|
||||
<Value>Miramax Films</Value>
|
||||
<Value>Momentum Pictures</Value>
|
||||
<Value>New Line Cinema</Value>
|
||||
<Value>New Line Home Entertainment</Value>
|
||||
<Value>Paramount Pictures</Value>
|
||||
<Value>Sony Pictures</Value>
|
||||
<Value>Sony Pictures Home Entertainment</Value>
|
||||
<Value>Spyglass Entertainment</Value>
|
||||
<Value>Studio Canal</Value>
|
||||
<Value>Summit Entertainment</Value>
|
||||
<Value>Touchstone Pictures</Value>
|
||||
<Value>Universal Pictures</Value>
|
||||
<Value>Universal Studios</Value>
|
||||
<Value>Universal Studios Home Entertainment</Value>
|
||||
<Value>Valhalla Motion Pictures</Value>
|
||||
<Value>Walt Disney Home Entertainment</Value>
|
||||
<Value>Walt Disney Pictures</Value>
|
||||
<Value>Warner Bros.</Value>
|
||||
<Value>Warner Bros. Entertainment</Value>
|
||||
<Value>Warner Bros. Pictures</Value>
|
||||
<Value>Weinstein Company</Value>
|
||||
<Value>Working Title Productions</Value>
|
||||
</MovieStudios>
|
||||
<MovieRatings>
|
||||
<Value>CS</Value>
|
||||
<Value>G</Value>
|
||||
<Value>NC-17</Value>
|
||||
<Value>NR</Value>
|
||||
<Value>PG</Value>
|
||||
<Value>PG-13</Value>
|
||||
<Value>R</Value>
|
||||
<Value>S</Value>
|
||||
</MovieRatings>
|
||||
<MovieCrewType>
|
||||
<Value>Art Director</Value>
|
||||
<Value>Assistant Director</Value>
|
||||
<Value>Associate Producer</Value>
|
||||
<Value>Background Artist</Value>
|
||||
<Value>Best Boy</Value>
|
||||
<Value>Body Double</Value>
|
||||
<Value>Boom Operator</Value>
|
||||
<Value>Camera Loader</Value>
|
||||
<Value>Casting Director</Value>
|
||||
<Value>Choreographer</Value>
|
||||
<Value>Cinematographer</Value>
|
||||
<Value>Color Consultant</Value>
|
||||
<Value>Composer</Value>
|
||||
<Value>Conductor</Value>
|
||||
<Value>Construction Coordinator</Value>
|
||||
<Value>Costume Designer</Value>
|
||||
<Value>Costumer</Value>
|
||||
<Value>Creator</Value>
|
||||
<Value>Dialog Coach</Value>
|
||||
<Value>Director</Value>
|
||||
<Value>Director of Photography</Value>
|
||||
<Value>Dolly Grip</Value>
|
||||
<Value>Editor</Value>
|
||||
<Value>Executive Producer</Value>
|
||||
<Value>Extra</Value>
|
||||
<Value>Foley Artist</Value>
|
||||
<Value>Gaffer</Value>
|
||||
<Value>Greensman</Value>
|
||||
<Value>Grip</Value>
|
||||
<Value>Key Grip</Value>
|
||||
<Value>Line Producer</Value>
|
||||
<Value>Location Manager</Value>
|
||||
<Value>Matte Artist</Value>
|
||||
<Value>Producer</Value>
|
||||
<Value>Production Assistant</Value>
|
||||
<Value>Production Illustrator</Value>
|
||||
<Value>Production Manager</Value>
|
||||
<Value>Property Master</Value>
|
||||
<Value>Screenwriter</Value>
|
||||
<Value>Set Decorator</Value>
|
||||
<Value>Set Designer</Value>
|
||||
<Value>Sound Designer</Value>
|
||||
<Value>Technical Advisor</Value>
|
||||
<Value>Unit Production Manager</Value>
|
||||
<Value>Wrangler</Value>
|
||||
</MovieCrewType>
|
||||
<TVNetworks>
|
||||
<Value>A&E</Value>
|
||||
<Value>ABC</Value>
|
||||
<Value>AMC</Value>
|
||||
<Value>BET</Value>
|
||||
<Value>BRAVO</Value>
|
||||
<Value>CBS</Value>
|
||||
<Value>CMDY</Value>
|
||||
<Value>DISC</Value>
|
||||
<Value>E!</Value>
|
||||
<Value>FOOD</Value>
|
||||
<Value>FOX</Value>
|
||||
<Value>HBO</Value>
|
||||
<Value>HGTV</Value>
|
||||
<Value>HIST</Value>
|
||||
<Value>LIFE</Value>
|
||||
<Value>MSNBC</Value>
|
||||
<Value>MTV</Value>
|
||||
<Value>MTV2</Value>
|
||||
<Value>NBC</Value>
|
||||
<Value>NICK</Value>
|
||||
<Value>SPIKE</Value>
|
||||
<Value>SPIKE</Value>
|
||||
<Value>SYFY</Value>
|
||||
<Value>TBS</Value>
|
||||
<Value>TLC</Value>
|
||||
<Value>TNT</Value>
|
||||
<Value>TOON</Value>
|
||||
<Value>TOONW</Value>
|
||||
<Value>TRUTV</Value>
|
||||
<Value>TVLND</Value>
|
||||
<Value>USA</Value>
|
||||
</TVNetworks>
|
||||
<TVRatings>
|
||||
<Value>CS</Value>
|
||||
<Value>TV-14</Value>
|
||||
<Value>TV-G</Value>
|
||||
<Value>TV-MA</Value>
|
||||
<Value>TV-PG</Value>
|
||||
<Value>TV-Y</Value>
|
||||
<Value>TV-Y7</Value>
|
||||
<Value>TV-Y7-FV</Value>
|
||||
</TVRatings>
|
||||
<VideoCodecs>
|
||||
<Value>ASF</Value>
|
||||
<Value>AVC</Value>
|
||||
<Value>DivX</Value>
|
||||
<Value>H.264</Value>
|
||||
<Value>MPEG-1</Value>
|
||||
<Value>MPEG-2</Value>
|
||||
<Value>RealVideo</Value>
|
||||
<Value>VC-1</Value>
|
||||
<Value>WMV</Value>
|
||||
<Value>XviD</Value>
|
||||
</VideoCodecs>
|
||||
<AudioCodecs>
|
||||
<Value>AAC</Value>
|
||||
<Value>AC-3</Value>
|
||||
<Value>DTS</Value>
|
||||
<Value>DTS-HD MA</Value>
|
||||
<Value>E-AC-3</Value>
|
||||
<Value>FLAC</Value>
|
||||
<Value>MP2</Value>
|
||||
<Value>MP3</Value>
|
||||
<Value>MPEG AUDIO</Value>
|
||||
<Value>PCM</Value>
|
||||
<Value>RealAudio</Value>
|
||||
<Value>TrueHD</Value>
|
||||
<Value>Vorbis</Value>
|
||||
<Value>WMA</Value>
|
||||
</AudioCodecs>
|
||||
</ValueLists>
|
||||
<Mappings>
|
||||
<Movies>
|
||||
</Movies>
|
||||
<TV>
|
||||
</TV>
|
||||
<Sorting>
|
||||
<Mapping Key="(?i)^shameless.*?$" MatchCase="False" ExactMatch="False" Type="Regex">Shameless (US)</Mapping>
|
||||
<Mapping Key="(?i)^castle.*?$" MatchCase="False" ExactMatch="False" Type="Regex">Castle (2009)</Mapping>
|
||||
<Mapping Key="(?i)^the.river.*?$" MatchCase="False" ExactMatch="False" Type="Regex">The River (2012)</Mapping>
|
||||
<Mapping Key="(?i)^parenthood.*?$" MatchCase="False" ExactMatch="False" Type="Regex">Parenthood (2010)</Mapping>
|
||||
<Mapping Key="(?i)^the.office.*?$" MatchCase="False" ExactMatch="False" Type="Regex">The Office (US)</Mapping>
|
||||
<Mapping Key="(?i)^smash.*?$" MatchCase="False" ExactMatch="False" Type="Regex">Smash (2012)</Mapping>
|
||||
<Mapping Key="(?i)^rob.*?$" MatchCase="False" ExactMatch="False" Type="Regex">¡Rob!</Mapping>
|
||||
<Mapping Key="(?i)^archer.*?$" MatchCase="False" ExactMatch="False" Type="Regex">Archer (2009)</Mapping>
|
||||
<Mapping Key="(?i)^once.upon.a.time.*?$" MatchCase="False" ExactMatch="False" Type="Regex">Once Upon a Time (2011)</Mapping>
|
||||
<Mapping Key="(?i)^lifes.too.short.*?$" MatchCase="False" ExactMatch="False" Type="Regex">Life's Too Short</Mapping>
|
||||
<Mapping Key="(?i)^eastbound.and.down.*?$" MatchCase="False" ExactMatch="False" Type="Regex">Eastbound & Down</Mapping>
|
||||
<Mapping Key="(?i)^the.killing.*?$" MatchCase="False" ExactMatch="False" Type="Regex">The Killing (2011)</Mapping>
|
||||
<Mapping Key="(?i)^touch.*?$" MatchCase="False" ExactMatch="False" Type="Regex">Touch (2012)</Mapping>
|
||||
<Mapping Key="(?i)^missing.*?$" MatchCase="False" ExactMatch="False" Type="Regex">Missing (2012)</Mapping>
|
||||
<Mapping Key="(?i)^scandal.*?$" MatchCase="False" ExactMatch="False" Type="Regex">Scandal (2012)</Mapping>
|
||||
<Mapping Key="(?i)^wilfred.*?$" MatchCase="False" ExactMatch="False" Type="Regex">Wilfred (US)</Mapping>
|
||||
<Mapping Key="(?i)^louie.*?$" MatchCase="False" ExactMatch="False" Type="Regex">Louie (2010)</Mapping>
|
||||
<Mapping Key="(?i)^the.newsroom.*?$" MatchCase="False" ExactMatch="False" Type="Regex">The Newsroom (2012)</Mapping>
|
||||
<Mapping Key="(?i)^boss.*?$" MatchCase="False" ExactMatch="False" Type="Regex">Boss (2011)</Mapping>
|
||||
</Sorting>
|
||||
<SortingFilename>
|
||||
</SortingFilename>
|
||||
<Type>
|
||||
<VIDEO_TS>DVD</VIDEO_TS>
|
||||
<BDMV>Blu-ray</BDMV>
|
||||
<HVDVD_TS>HD DVD</HVDVD_TS>
|
||||
</Type>
|
||||
</Mappings>
|
||||
<WebService>
|
||||
<LoadOnStartup>False</LoadOnStartup>
|
||||
<Port>8085</Port>
|
||||
<PasswordProtected>False</PasswordProtected>
|
||||
<Username>
|
||||
</Username>
|
||||
<SortByDateAdded>False</SortByDateAdded>
|
||||
<Password>
|
||||
</Password>
|
||||
<AllowedPathsEnabled>False</AllowedPathsEnabled>
|
||||
</WebService>
|
||||
<MetadataVisibleFields>
|
||||
<movies>
|
||||
<LocalTitle>True</LocalTitle>
|
||||
<OriginalTitle>True</OriginalTitle>
|
||||
<SortTitle>True</SortTitle>
|
||||
<movieSet>True</movieSet>
|
||||
<DateAdded>True</DateAdded>
|
||||
<ProductionYear>True</ProductionYear>
|
||||
<Runtime>True</Runtime>
|
||||
<Rating>True</Rating>
|
||||
<MPAARating>True</MPAARating>
|
||||
<MPAADescription>True</MPAADescription>
|
||||
<CustomRating>True</CustomRating>
|
||||
<Plot>True</Plot>
|
||||
<Description>True</Description>
|
||||
<Type>True</Type>
|
||||
<AspectRatio>True</AspectRatio>
|
||||
<Watched>True</Watched>
|
||||
<Comment>True</Comment>
|
||||
<AllowRenaming>True</AllowRenaming>
|
||||
<CollectionNumber>True</CollectionNumber>
|
||||
<TrailerUrl>True</TrailerUrl>
|
||||
<IMDbId>True</IMDbId>
|
||||
<TMDbId>True</TMDbId>
|
||||
<MyMoviesId>True</MyMoviesId>
|
||||
<NetflixId>True</NetflixId>
|
||||
<MovieMeterId>True</MovieMeterId>
|
||||
<AlloCineId>True</AlloCineId>
|
||||
<FilmAffinityId>True</FilmAffinityId>
|
||||
<YahooIndiaId>True</YahooIndiaId>
|
||||
<AmazonId>True</AmazonId>
|
||||
<RottenTomatoesId>True</RottenTomatoesId>
|
||||
<CineFactsId>True</CineFactsId>
|
||||
<OFDbId>True</OFDbId>
|
||||
<CSFDId>True</CSFDId>
|
||||
<MoviePlayerId>True</MoviePlayerId>
|
||||
<AdultDVDEmpireId>True</AdultDVDEmpireId>
|
||||
<CDUniverseId>True</CDUniverseId>
|
||||
</movies>
|
||||
<series>
|
||||
<SeriesName>True</SeriesName>
|
||||
<AirDay>True</AirDay>
|
||||
<AirTime>True</AirTime>
|
||||
<Runtime>True</Runtime>
|
||||
<Network>True</Network>
|
||||
<MPAARating>True</MPAARating>
|
||||
<CustomRating>True</CustomRating>
|
||||
<Status>True</Status>
|
||||
<FirstAirDate>True</FirstAirDate>
|
||||
<Description>True</Description>
|
||||
<Rating>True</Rating>
|
||||
<Language>True</Language>
|
||||
<Comment>True</Comment>
|
||||
<AllowRenaming>True</AllowRenaming>
|
||||
<CustomRenamePattern>True</CustomRenamePattern>
|
||||
<DisplayOrder>True</DisplayOrder>
|
||||
<FetchOrder>True</FetchOrder>
|
||||
<IMDbId>True</IMDbId>
|
||||
<TVDbId>True</TVDbId>
|
||||
<TVcomId>True</TVcomId>
|
||||
<TVcom2Id>True</TVcom2Id>
|
||||
<MoviePlayerId>True</MoviePlayerId>
|
||||
</series>
|
||||
<episode>
|
||||
<EpisodeName>True</EpisodeName>
|
||||
<SeasonNumber>True</SeasonNumber>
|
||||
<EpisodeNumber>True</EpisodeNumber>
|
||||
<DVDSeasonNumber>True</DVDSeasonNumber>
|
||||
<DVDEpisodeNumber>True</DVDEpisodeNumber>
|
||||
<AbsoluteEpisodeNumber>True</AbsoluteEpisodeNumber>
|
||||
<AirsAfterSeason>True</AirsAfterSeason>
|
||||
<AirsBeforeEpisode>True</AirsBeforeEpisode>
|
||||
<AirsBeforeSeason>True</AirsBeforeSeason>
|
||||
<DateAdded>True</DateAdded>
|
||||
<FirstAirDate>True</FirstAirDate>
|
||||
<GuestStars>True</GuestStars>
|
||||
<Description>True</Description>
|
||||
<Writer>True</Writer>
|
||||
<Director>True</Director>
|
||||
<Rating>True</Rating>
|
||||
<Type>True</Type>
|
||||
<Watched>True</Watched>
|
||||
<CustomRating>True</CustomRating>
|
||||
<Comment>True</Comment>
|
||||
</episode>
|
||||
<season>
|
||||
<Description>True</Description>
|
||||
<Comment>True</Comment>
|
||||
<CustomRating>True</CustomRating>
|
||||
</season>
|
||||
<music>
|
||||
<Title>True</Title>
|
||||
<Album>True</Album>
|
||||
<AlbumArtist>True</AlbumArtist>
|
||||
<TitleSort>True</TitleSort>
|
||||
<AlbumSort>True</AlbumSort>
|
||||
<Performer>True</Performer>
|
||||
<DateAdded>True</DateAdded>
|
||||
<Year>True</Year>
|
||||
<Genre>True</Genre>
|
||||
<Composer>True</Composer>
|
||||
<Track>True</Track>
|
||||
<TrackCount>True</TrackCount>
|
||||
<Disc>True</Disc>
|
||||
<DiscCount>True</DiscCount>
|
||||
<Comment>True</Comment>
|
||||
<Copyright>True</Copyright>
|
||||
<Publisher>True</Publisher>
|
||||
<Grouping>True</Grouping>
|
||||
<MusicBrainzTrackId>True</MusicBrainzTrackId>
|
||||
<MusicBrainzReleaseId>True</MusicBrainzReleaseId>
|
||||
</music>
|
||||
</MetadataVisibleFields>
|
||||
<Misc>
|
||||
<MediaIconsLocation>Poster</MediaIconsLocation>
|
||||
<MediaIconsPosition>top</MediaIconsPosition>
|
||||
<MediaIconsOpacity>50</MediaIconsOpacity>
|
||||
<FFmpegLocation>
|
||||
</FFmpegLocation>
|
||||
<FFProbeLocation>
|
||||
</FFProbeLocation>
|
||||
<UseDisplayAspectRatio>False</UseDisplayAspectRatio>
|
||||
<ColorDefault>-16777216</ColorDefault>
|
||||
<ColorNoMetadata>-65536</ColorNoMetadata>
|
||||
<ColorIncompleteMetadata>-16776961</ColorIncompleteMetadata>
|
||||
<ColorCompleteMetadata>-16744448</ColorCompleteMetadata>
|
||||
<HighlightParent>True</HighlightParent>
|
||||
<ShowRedDots>True</ShowRedDots>
|
||||
<ShowAsCompleteWhenLocked>True</ShowAsCompleteWhenLocked>
|
||||
<LoadWithCollapsedGroups>False</LoadWithCollapsedGroups>
|
||||
<UseTieredExpandCollapse>False</UseTieredExpandCollapse>
|
||||
<DoNotExceedOriginalImageSizeOnPanel>True</DoNotExceedOriginalImageSizeOnPanel>
|
||||
<SkipCleanupAfterManualSave>False</SkipCleanupAfterManualSave>
|
||||
<ResumePauseSeconds>0</ResumePauseSeconds>
|
||||
<DefaultExternalSubLanguage>
|
||||
</DefaultExternalSubLanguage>
|
||||
<AllowMultiSelect>True</AllowMultiSelect>
|
||||
<AsyncMultiSelectMinimum>50</AsyncMultiSelectMinimum>
|
||||
<AsyncSaveMinimum>1</AsyncSaveMinimum>
|
||||
</Misc>
|
||||
<Movies>
|
||||
<AllowMultipleMoviesPerFolder>False</AllowMultipleMoviesPerFolder>
|
||||
<AppendProductionYear>True</AppendProductionYear>
|
||||
<GroupMovieSets>True</GroupMovieSets>
|
||||
<FlattenFolders>False</FlattenFolders>
|
||||
<UseFolderNameForSortTitle>False</UseFolderNameForSortTitle>
|
||||
<SelectFirstSearchResult>False</SelectFirstSearchResult>
|
||||
<DisplayValuePattern>%lt</DisplayValuePattern>
|
||||
<SortValue>SortTitle</SortValue>
|
||||
<FetchValue>OriginalTitle</FetchValue>
|
||||
<PreferredPosterPlugin>7291961d-21e7-4ee2-a996-4febdb7661eb</PreferredPosterPlugin>
|
||||
<UsePreferredPosterPluginOnly>True</UsePreferredPosterPluginOnly>
|
||||
<PosterMinimumWidth>0</PosterMinimumWidth>
|
||||
<PosterMinimumHeight>0</PosterMinimumHeight>
|
||||
<PostersToDownload>1</PostersToDownload>
|
||||
<PreferredBackdropPlugin>546eda50-7029-4421-9596-09b2bae293f7</PreferredBackdropPlugin>
|
||||
<UsePreferredBackdropPluginOnly>True</UsePreferredBackdropPluginOnly>
|
||||
<BackdropMinimumWidth>1920</BackdropMinimumWidth>
|
||||
<BackdropMinimumHeight>0</BackdropMinimumHeight>
|
||||
<BackdropsToDownload>3</BackdropsToDownload>
|
||||
<PreferredClearArtPlugin>01a001e8-316b-4e49-8e9a-52b5b3179067</PreferredClearArtPlugin>
|
||||
<UsePreferredClearArtPluginOnly>False</UsePreferredClearArtPluginOnly>
|
||||
<PreferredCastPlugin>c422bc7f-2910-4e62-9370-a5ba5ee69514</PreferredCastPlugin>
|
||||
<UsePreferredCastPluginOnly>False</UsePreferredCastPluginOnly>
|
||||
<PreferredTrailerPlugin>d3dd7a50-859f-4bcd-91c9-51e218ce29eb</PreferredTrailerPlugin>
|
||||
<UsePreferredTrailerPluginOnly>False</UsePreferredTrailerPluginOnly>
|
||||
<PreferredTrailerResolution>0</PreferredTrailerResolution>
|
||||
<DownloadNextAvailableTrailerResolution>True</DownloadNextAvailableTrailerResolution>
|
||||
<DeleteTrailerFromCache>True</DeleteTrailerFromCache>
|
||||
<DownloadAllTrailersIncludeLocked>False</DownloadAllTrailersIncludeLocked>
|
||||
<UseMediaInfoRuntime>True</UseMediaInfoRuntime>
|
||||
<RenameMovie>False</RenameMovie>
|
||||
<RenameMovieFolder>False</RenameMovieFolder>
|
||||
<RenameMoviePattern>
|
||||
</RenameMoviePattern>
|
||||
<RenameMovieFolderPattern>
|
||||
</RenameMovieFolderPattern>
|
||||
<UpdateFields>
|
||||
<FetchIdOnly>False</FetchIdOnly>
|
||||
<ReplaceMissingOnly>False</ReplaceMissingOnly>
|
||||
<Information>True</Information>
|
||||
<Posters>True</Posters>
|
||||
<Backdrops>True</Backdrops>
|
||||
<Logos>True</Logos>
|
||||
<ClearArts>True</ClearArts>
|
||||
<Trailers>False</Trailers>
|
||||
<Rename>True</Rename>
|
||||
<LocalTitle>True</LocalTitle>
|
||||
<OriginalTitle>True</OriginalTitle>
|
||||
<SortTitle>True</SortTitle>
|
||||
<ProductionYear>True</ProductionYear>
|
||||
<Runtime>True</Runtime>
|
||||
<Rating>True</Rating>
|
||||
<MPAARating>True</MPAARating>
|
||||
<MPAADescription>True</MPAADescription>
|
||||
<Plot>True</Plot>
|
||||
<Description>True</Description>
|
||||
<Type>True</Type>
|
||||
<AspectRatio>True</AspectRatio>
|
||||
<TrailerUrl>True</TrailerUrl>
|
||||
<Watched>True</Watched>
|
||||
<CastCrew>True</CastCrew>
|
||||
<CastCrewImages>False</CastCrewImages>
|
||||
<Genres>True</Genres>
|
||||
<Studios>True</Studios>
|
||||
<Countries>True</Countries>
|
||||
<Taglines>True</Taglines>
|
||||
<MediaInfoCustom>True</MediaInfoCustom>
|
||||
</UpdateFields>
|
||||
<MetadataCompleteFields>
|
||||
<LocalTitle>True</LocalTitle>
|
||||
<OriginalTitle>True</OriginalTitle>
|
||||
<SortTitle>True</SortTitle>
|
||||
<ProductionYear>True</ProductionYear>
|
||||
<Runtime>True</Runtime>
|
||||
<Rating>True</Rating>
|
||||
<MPAARating>True</MPAARating>
|
||||
<MPAADescription>False</MPAADescription>
|
||||
<CustomRating>False</CustomRating>
|
||||
<Plot>False</Plot>
|
||||
<Description>True</Description>
|
||||
<Type>True</Type>
|
||||
<AspectRatio>True</AspectRatio>
|
||||
<TrailerUrl>True</TrailerUrl>
|
||||
<Watched>False</Watched>
|
||||
<Comment>False</Comment>
|
||||
<CollectionNumber>False</CollectionNumber>
|
||||
<CastCrew>True</CastCrew>
|
||||
<Genres>True</Genres>
|
||||
<Studios>False</Studios>
|
||||
<Countries>False</Countries>
|
||||
<Taglines>True</Taglines>
|
||||
<MediaInfoCustom>True</MediaInfoCustom>
|
||||
<Trailers>True</Trailers>
|
||||
<Posters>True</Posters>
|
||||
<Backdrops>True</Backdrops>
|
||||
<Logos>True</Logos>
|
||||
<ClearArts>False</ClearArts>
|
||||
</MetadataCompleteFields>
|
||||
<Plugins>
|
||||
<Locals>
|
||||
<Id Order="0" State="0">529c3819-0ca4-4741-b6b5-48360ace62ee</Id>
|
||||
</Locals>
|
||||
<Fetchers>
|
||||
<RealTime>6d06642d-d028-4b10-a6fd-3060637e9883</RealTime>
|
||||
<LocalTitle>6d06642d-d028-4b10-a6fd-3060637e9883</LocalTitle>
|
||||
<OriginalTitle>6d06642d-d028-4b10-a6fd-3060637e9883</OriginalTitle>
|
||||
<SortTitle>6d06642d-d028-4b10-a6fd-3060637e9883</SortTitle>
|
||||
<ProductionYear>6d06642d-d028-4b10-a6fd-3060637e9883</ProductionYear>
|
||||
<Runtime>6d06642d-d028-4b10-a6fd-3060637e9883</Runtime>
|
||||
<Rating>6d06642d-d028-4b10-a6fd-3060637e9883</Rating>
|
||||
<MPAARating>6d06642d-d028-4b10-a6fd-3060637e9883</MPAARating>
|
||||
<MPAADescription>6d06642d-d028-4b10-a6fd-3060637e9883</MPAADescription>
|
||||
<Plot>6d06642d-d028-4b10-a6fd-3060637e9883</Plot>
|
||||
<Description>6d06642d-d028-4b10-a6fd-3060637e9883</Description>
|
||||
<AspectRatio>6d06642d-d028-4b10-a6fd-3060637e9883</AspectRatio>
|
||||
<TrailerUrl>6d06642d-d028-4b10-a6fd-3060637e9883</TrailerUrl>
|
||||
<Watched>6d06642d-d028-4b10-a6fd-3060637e9883</Watched>
|
||||
<CastCrew>6d06642d-d028-4b10-a6fd-3060637e9883</CastCrew>
|
||||
<Genres>6d06642d-d028-4b10-a6fd-3060637e9883</Genres>
|
||||
<Studios>6d06642d-d028-4b10-a6fd-3060637e9883</Studios>
|
||||
<Countries>6d06642d-d028-4b10-a6fd-3060637e9883</Countries>
|
||||
<Taglines>6d06642d-d028-4b10-a6fd-3060637e9883</Taglines>
|
||||
</Fetchers>
|
||||
<Savers>
|
||||
<Id Order="0" State="1">a04f5745-d062-4e14-bc36-24a673cfed22</Id>
|
||||
<Id Order="1" State="1">d04f5745-d062-4e14-bd36-24a673cfed22</Id>
|
||||
</Savers>
|
||||
</Plugins>
|
||||
<Trailers>
|
||||
<DownloadPath>D:\Video\Coming Soon\</DownloadPath>
|
||||
<MaxTrailers>1000</MaxTrailers>
|
||||
<Quality>HD</Quality>
|
||||
<OnlyDownloadNewerThanLastPostdate>False</OnlyDownloadNewerThanLastPostdate>
|
||||
<CreateFolders>True</CreateFolders>
|
||||
<FetchMetadata>True</FetchMetadata>
|
||||
<PopulateMPAARating>True</PopulateMPAARating>
|
||||
<DeleteTrailers>True</DeleteTrailers>
|
||||
<DeleteTrailersDays>120</DeleteTrailersDays>
|
||||
<DeleteTrailersLowerLimit>25</DeleteTrailersLowerLimit>
|
||||
<SchedulerEnabled>True</SchedulerEnabled>
|
||||
<SchedulerDay>-1</SchedulerDay>
|
||||
<SchedulerTime>2/1/0001 12:00:00 AM</SchedulerTime>
|
||||
<RenameTrailer>True</RenameTrailer>
|
||||
<RenameTrailerPattern>%ot (%py).%ext</RenameTrailerPattern>
|
||||
<SkipGenres>
|
||||
</SkipGenres>
|
||||
</Trailers>
|
||||
</Movies>
|
||||
<TV>
|
||||
<ValidSeasons>Season;Series;Specials</ValidSeasons>
|
||||
<SortValue>Default</SortValue>
|
||||
<EnableSeasonLevelBanners>False</EnableSeasonLevelBanners>
|
||||
<RefreshLocked>True</RefreshLocked>
|
||||
<PreferredPosterPlugin>fcb4d2d3-c609-432a-8c51-25136d847a32</PreferredPosterPlugin>
|
||||
<UsePreferredPosterPluginOnly>True</UsePreferredPosterPluginOnly>
|
||||
<PosterMinimumWidth>0</PosterMinimumWidth>
|
||||
<PosterMinimumHeight>0</PosterMinimumHeight>
|
||||
<PostersToDownload>1</PostersToDownload>
|
||||
<ExtractEpisodeImages>False</ExtractEpisodeImages>
|
||||
<OnlyExtractEpisodeImages>False</OnlyExtractEpisodeImages>
|
||||
<PreferredBackdropPlugin>621f9839-4750-4ceb-a286-06fe96cd7f98</PreferredBackdropPlugin>
|
||||
<UsePreferredBackdropPluginOnly>True</UsePreferredBackdropPluginOnly>
|
||||
<BackdropMinimumWidth>1920</BackdropMinimumWidth>
|
||||
<BackdropMinimumHeight>0</BackdropMinimumHeight>
|
||||
<BackdropsToDownload>3</BackdropsToDownload>
|
||||
<PreferredBannerPlugin>36e97d3d-fa00-43d6-b809-ef3595f0b5da</PreferredBannerPlugin>
|
||||
<UsePreferredBannerPluginOnly>True</UsePreferredBannerPluginOnly>
|
||||
<BannersToDownload>1</BannersToDownload>
|
||||
<PreferredClearArtPlugin>01a001e8-316b-4e49-8e9a-52b5b3179067</PreferredClearArtPlugin>
|
||||
<UsePreferredClearArtPluginOnly>False</UsePreferredClearArtPluginOnly>
|
||||
<PreferredCastPlugin>c422bc7f-2910-4e62-9370-a5ba5ee69514</PreferredCastPlugin>
|
||||
<UsePreferredCastPluginOnly>False</UsePreferredCastPluginOnly>
|
||||
<RenameTV>True</RenameTV>
|
||||
<RenameTVPattern>%sn - %sx%0e - %en.%ext</RenameTVPattern>
|
||||
<UpdateFields>
|
||||
<Series>
|
||||
<FetchIdOnly>False</FetchIdOnly>
|
||||
<ReplaceMissingOnly>False</ReplaceMissingOnly>
|
||||
<Information>True</Information>
|
||||
<Posters>True</Posters>
|
||||
<Backdrops>True</Backdrops>
|
||||
<Banners>True</Banners>
|
||||
<Logos>True</Logos>
|
||||
<ClearArts>True</ClearArts>
|
||||
<Thumbs>True</Thumbs>
|
||||
<SeriesName>True</SeriesName>
|
||||
<AirDay>True</AirDay>
|
||||
<FirstAirDate>True</FirstAirDate>
|
||||
<AirTime>True</AirTime>
|
||||
<Runtime>True</Runtime>
|
||||
<Rating>True</Rating>
|
||||
<MPAARating>True</MPAARating>
|
||||
<Description>True</Description>
|
||||
<Network>True</Network>
|
||||
<Status>True</Status>
|
||||
<CastCrew>True</CastCrew>
|
||||
<CastCrewImages>False</CastCrewImages>
|
||||
<Genres>True</Genres>
|
||||
</Series>
|
||||
<Season>
|
||||
<Posters>True</Posters>
|
||||
<Backdrops>False</Backdrops>
|
||||
<Banners>True</Banners>
|
||||
<Thumbs>True</Thumbs>
|
||||
</Season>
|
||||
<Episode>
|
||||
<ReplaceMissingOnly>False</ReplaceMissingOnly>
|
||||
<Information>True</Information>
|
||||
<Posters>True</Posters>
|
||||
<Rename>True</Rename>
|
||||
<EpisodeName>True</EpisodeName>
|
||||
<FirstAirDate>True</FirstAirDate>
|
||||
<GuestStars>True</GuestStars>
|
||||
<Description>True</Description>
|
||||
<Writer>True</Writer>
|
||||
<Director>True</Director>
|
||||
<Rating>True</Rating>
|
||||
<Type>True</Type>
|
||||
<Watched>True</Watched>
|
||||
<MediaInfoCustom>True</MediaInfoCustom>
|
||||
<CastCrewImages>True</CastCrewImages>
|
||||
</Episode>
|
||||
</UpdateFields>
|
||||
<MetadataCompleteFields>
|
||||
<Series>
|
||||
<SeriesName>True</SeriesName>
|
||||
<AirDay>False</AirDay>
|
||||
<FirstAirDate>True</FirstAirDate>
|
||||
<AirTime>False</AirTime>
|
||||
<Runtime>True</Runtime>
|
||||
<Rating>True</Rating>
|
||||
<MPAARating>False</MPAARating>
|
||||
<Description>True</Description>
|
||||
<Network>True</Network>
|
||||
<Status>True</Status>
|
||||
<CastCrew>True</CastCrew>
|
||||
<Genres>True</Genres>
|
||||
<Comment>False</Comment>
|
||||
<Posters>True</Posters>
|
||||
<Backdrops>True</Backdrops>
|
||||
<Banners>True</Banners>
|
||||
<Logos>True</Logos>
|
||||
<ClearArts>False</ClearArts>
|
||||
<Thumbs>False</Thumbs>
|
||||
</Series>
|
||||
<Season>
|
||||
<Description>False</Description>
|
||||
<Comment>False</Comment>
|
||||
<Posters>True</Posters>
|
||||
<Backdrops>False</Backdrops>
|
||||
<Banners>False</Banners>
|
||||
<Thumbs>False</Thumbs>
|
||||
</Season>
|
||||
<Episode>
|
||||
<EpisodeName>True</EpisodeName>
|
||||
<FirstAirDate>False</FirstAirDate>
|
||||
<GuestStars>False</GuestStars>
|
||||
<Description>True</Description>
|
||||
<Writer>False</Writer>
|
||||
<Director>False</Director>
|
||||
<Rating>False</Rating>
|
||||
<Type>False</Type>
|
||||
<Watched>False</Watched>
|
||||
<Comment>False</Comment>
|
||||
<MediaInfoCustom>False</MediaInfoCustom>
|
||||
<Posters>True</Posters>
|
||||
</Episode>
|
||||
</MetadataCompleteFields>
|
||||
<Plugins>
|
||||
<Locals>
|
||||
<Id Order="0" State="0">5b118517-cc37-427c-bf03-34dec95db959</Id>
|
||||
</Locals>
|
||||
<Fetchers>
|
||||
<Series>
|
||||
<RealTime>default</RealTime>
|
||||
<SeriesName>29f3e4f0-5a4b-4462-a0fe-45f0593ca5f1</SeriesName>
|
||||
<AirDay>29f3e4f0-5a4b-4462-a0fe-45f0593ca5f1</AirDay>
|
||||
<FirstAirDate>29f3e4f0-5a4b-4462-a0fe-45f0593ca5f1</FirstAirDate>
|
||||
<AirTime>29f3e4f0-5a4b-4462-a0fe-45f0593ca5f1</AirTime>
|
||||
<Runtime>29f3e4f0-5a4b-4462-a0fe-45f0593ca5f1</Runtime>
|
||||
<Rating>29f3e4f0-5a4b-4462-a0fe-45f0593ca5f1</Rating>
|
||||
<MPAARating>29f3e4f0-5a4b-4462-a0fe-45f0593ca5f1</MPAARating>
|
||||
<Description>29f3e4f0-5a4b-4462-a0fe-45f0593ca5f1</Description>
|
||||
<Network>29f3e4f0-5a4b-4462-a0fe-45f0593ca5f1</Network>
|
||||
<Status>29f3e4f0-5a4b-4462-a0fe-45f0593ca5f1</Status>
|
||||
<CastCrew>29f3e4f0-5a4b-4462-a0fe-45f0593ca5f1</CastCrew>
|
||||
<Genres>29f3e4f0-5a4b-4462-a0fe-45f0593ca5f1</Genres>
|
||||
</Series>
|
||||
<Episode>
|
||||
<EpisodeName>29f3e4f0-5a4b-4462-a0fe-45f0593ca5f1</EpisodeName>
|
||||
<FirstAirDate>29f3e4f0-5a4b-4462-a0fe-45f0593ca5f1</FirstAirDate>
|
||||
<GuestStars>29f3e4f0-5a4b-4462-a0fe-45f0593ca5f1</GuestStars>
|
||||
<Description>29f3e4f0-5a4b-4462-a0fe-45f0593ca5f1</Description>
|
||||
<Writer>29f3e4f0-5a4b-4462-a0fe-45f0593ca5f1</Writer>
|
||||
<Director>29f3e4f0-5a4b-4462-a0fe-45f0593ca5f1</Director>
|
||||
<Rating>29f3e4f0-5a4b-4462-a0fe-45f0593ca5f1</Rating>
|
||||
<Watched>29f3e4f0-5a4b-4462-a0fe-45f0593ca5f1</Watched>
|
||||
</Episode>
|
||||
</Fetchers>
|
||||
<Savers>
|
||||
<Id Order="0" State="1">59e2f1dc-8dc7-49a7-9e47-29ce066084c3</Id>
|
||||
</Savers>
|
||||
</Plugins>
|
||||
<Sorting>
|
||||
<MinimumFileSize>50</MinimumFileSize>
|
||||
<AutoPoll>True</AutoPoll>
|
||||
<DeleteEmptyFolders>False</DeleteEmptyFolders>
|
||||
<TransferMethod>move</TransferMethod>
|
||||
<MoveAccompanyingFiles>True</MoveAccompanyingFiles>
|
||||
<NotifyMCEClients>False</NotifyMCEClients>
|
||||
<DeleteLeftOverFiles>False</DeleteLeftOverFiles>
|
||||
<LeftOverFilesExtensions>.nfo;.txt</LeftOverFilesExtensions>
|
||||
<SeasonFolderPattern>Season %s</SeasonFolderPattern>
|
||||
<SeasonZeroFolderPattern>Season %0s</SeasonZeroFolderPattern>
|
||||
<OnlyCreateSeriesFolderForS1E1>True</OnlyCreateSeriesFolderForS1E1>
|
||||
<MoveDuplicateEpisodes>False</MoveDuplicateEpisodes>
|
||||
<MoveDuplicateEpisodesLocation>D:\Temp\</MoveDuplicateEpisodesLocation>
|
||||
<OverwiteExistingEpisodes>True</OverwiteExistingEpisodes>
|
||||
<OverwriteOnResolution>False</OverwriteOnResolution>
|
||||
<OverwriteOnWords>False</OverwriteOnWords>
|
||||
<OverwriteWords>PROPER;REPACK</OverwriteWords>
|
||||
<CreateFolders>True</CreateFolders>
|
||||
<ConfirmBeforeProcessing>True</ConfirmBeforeProcessing>
|
||||
<Monitored location="D:\Temp\_MetaBrowserWatcher\" type="0">
|
||||
<Destination>D:\Video\TV\</Destination>
|
||||
</Monitored>
|
||||
</Sorting>
|
||||
<Schedule enabled="True" autofilterseries="False">
|
||||
</Schedule>
|
||||
</TV>
|
||||
<Music>
|
||||
<Enabled>False</Enabled>
|
||||
<SortValue>Default</SortValue>
|
||||
<RenameMusic>False</RenameMusic>
|
||||
<RenameMusicPattern>
|
||||
</RenameMusicPattern>
|
||||
<UpdateFields>
|
||||
<FetchIdOnly>False</FetchIdOnly>
|
||||
<ReplaceMissingOnly>False</ReplaceMissingOnly>
|
||||
<Information>True</Information>
|
||||
<Posters>True</Posters>
|
||||
<Rename>True</Rename>
|
||||
<Title>True</Title>
|
||||
<TitleSort>True</TitleSort>
|
||||
<Album>True</Album>
|
||||
<AlbumSort>True</AlbumSort>
|
||||
<AlbumArtist>True</AlbumArtist>
|
||||
<Performer>True</Performer>
|
||||
<Track>True</Track>
|
||||
<TrackCount>True</TrackCount>
|
||||
<Disc>False</Disc>
|
||||
<DiscCount>True</DiscCount>
|
||||
<Year>True</Year>
|
||||
<Genre>True</Genre>
|
||||
<Comment>True</Comment>
|
||||
<Composer>True</Composer>
|
||||
<Publisher>True</Publisher>
|
||||
<Copyright>True</Copyright>
|
||||
<Lyrics>True</Lyrics>
|
||||
</UpdateFields>
|
||||
<MetadataCompleteFields>
|
||||
<Title>True</Title>
|
||||
<TitleSort>False</TitleSort>
|
||||
<Album>True</Album>
|
||||
<AlbumSort>False</AlbumSort>
|
||||
<AlbumArtist>True</AlbumArtist>
|
||||
<Performer>False</Performer>
|
||||
<Track>True</Track>
|
||||
<TrackCount>False</TrackCount>
|
||||
<Disc>False</Disc>
|
||||
<DiscCount>False</DiscCount>
|
||||
<Year>True</Year>
|
||||
<Genre>True</Genre>
|
||||
<Comment>False</Comment>
|
||||
<Composer>False</Composer>
|
||||
<Publisher>False</Publisher>
|
||||
<Copyright>False</Copyright>
|
||||
<Lyrics>False</Lyrics>
|
||||
<Posters>True</Posters>
|
||||
</MetadataCompleteFields>
|
||||
<Plugins>
|
||||
<Locals>
|
||||
</Locals>
|
||||
<Fetchers>
|
||||
<RealTime>00000000-0000-0000-0000-000000000000</RealTime>
|
||||
<Title>00000000-0000-0000-0000-000000000000</Title>
|
||||
<TitleSort>00000000-0000-0000-0000-000000000000</TitleSort>
|
||||
<Album>00000000-0000-0000-0000-000000000000</Album>
|
||||
<AlbumSort>00000000-0000-0000-0000-000000000000</AlbumSort>
|
||||
<AlbumArtist>00000000-0000-0000-0000-000000000000</AlbumArtist>
|
||||
<Performer>00000000-0000-0000-0000-000000000000</Performer>
|
||||
<Track>00000000-0000-0000-0000-000000000000</Track>
|
||||
<TrackCount>00000000-0000-0000-0000-000000000000</TrackCount>
|
||||
<Disc>00000000-0000-0000-0000-000000000000</Disc>
|
||||
<DiscCount>00000000-0000-0000-0000-000000000000</DiscCount>
|
||||
<Year>00000000-0000-0000-0000-000000000000</Year>
|
||||
<Genre>00000000-0000-0000-0000-000000000000</Genre>
|
||||
<Comment>00000000-0000-0000-0000-000000000000</Comment>
|
||||
<Composer>00000000-0000-0000-0000-000000000000</Composer>
|
||||
<Publisher>00000000-0000-0000-0000-000000000000</Publisher>
|
||||
<Copyright>00000000-0000-0000-0000-000000000000</Copyright>
|
||||
<Lyrics>00000000-0000-0000-0000-000000000000</Lyrics>
|
||||
</Fetchers>
|
||||
<Savers>
|
||||
</Savers>
|
||||
</Plugins>
|
||||
</Music>
|
||||
</Options>
|
@ -1,6 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Rx-Core" version="2.0.20823" targetFramework="net45" />
|
||||
<package id="Rx-Interfaces" version="2.0.20823" targetFramework="net45" />
|
||||
<package id="Rx-Linq" version="2.0.20823" targetFramework="net45" />
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="morelinq" version="1.0.15631-beta" targetFramework="net45" />
|
||||
<package id="protobuf-net" version="2.0.0.621" targetFramework="net45" />
|
||||
<package id="Rx-Core" version="2.0.21114" targetFramework="net45" />
|
||||
<package id="Rx-Interfaces" version="2.0.21114" targetFramework="net45" />
|
||||
<package id="Rx-Linq" version="2.0.21114" targetFramework="net45" />
|
||||
<package id="ServiceStack" version="3.9.37" targetFramework="net45" />
|
||||
<package id="ServiceStack.Common" version="3.9.37" targetFramework="net45" />
|
||||
<package id="ServiceStack.OrmLite.SqlServer" version="3.9.37" targetFramework="net45" />
|
||||
<package id="ServiceStack.Redis" version="3.9.37" targetFramework="net45" />
|
||||
<package id="ServiceStack.Text" version="3.9.37" targetFramework="net45" />
|
||||
</packages>
|
1360
MediaBrowser.ApiInteraction.Javascript/ApiClient.js
Normal file
1360
MediaBrowser.ApiInteraction.Javascript/ApiClient.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,60 @@
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Net;
|
||||
using ServiceStack.ServiceHost;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.ApiInteraction.Javascript
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetJavascriptApiClient
|
||||
/// </summary>
|
||||
[Route("/JsApiClient.js", "GET")]
|
||||
[Api(("Gets an api wrapper in Javascript"))]
|
||||
public class GetJavascriptApiClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Version identifier for caching
|
||||
/// </summary>
|
||||
/// <value>The v.</value>
|
||||
public string V { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class JavascriptApiClientService
|
||||
/// </summary>
|
||||
[Export(typeof(IRestfulService))]
|
||||
public class JavascriptApiClientService : BaseRestService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetJavascriptApiClient request)
|
||||
{
|
||||
TimeSpan? cacheDuration = null;
|
||||
|
||||
// If there's a version number in the query string we can cache this unconditionally
|
||||
if (!string.IsNullOrEmpty(request.V))
|
||||
{
|
||||
cacheDuration = TimeSpan.FromDays(365);
|
||||
}
|
||||
|
||||
var assembly = GetType().Assembly.GetName();
|
||||
|
||||
return ToStaticResult(assembly.Version.ToString().GetMD5(), null, cacheDuration, MimeTypes.GetMimeType("script.js"), GetStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the stream.
|
||||
/// </summary>
|
||||
/// <returns>Stream.</returns>
|
||||
private Task<Stream> GetStream()
|
||||
{
|
||||
return Task.FromResult(GetType().Assembly.GetManifestResourceStream("MediaBrowser.ApiInteraction.Javascript.ApiClient.js"));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{767B536E-D90C-4D74-A14B-8564B16F3499}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MediaBrowser.ApiInteraction.Javascript</RootNamespace>
|
||||
<AssemblyName>MediaBrowser.ApiInteraction.Javascript</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||
<RestorePackages>true</RestorePackages>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RunPostBuildEvent>Always</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="ServiceStack, Version=3.9.37.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\ServiceStack.3.9.37\lib\net35\ServiceStack.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ServiceStack.Common, Version=3.9.37.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\ServiceStack.Common.3.9.37\lib\net35\ServiceStack.Common.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ServiceStack.Interfaces, Version=3.9.37.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\ServiceStack.Common.3.9.37\lib\net35\ServiceStack.Interfaces.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ServiceStack.OrmLite, Version=3.9.37.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\ServiceStack.OrmLite.SqlServer.3.9.37\lib\ServiceStack.OrmLite.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ServiceStack.OrmLite.SqlServer, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\ServiceStack.OrmLite.SqlServer.3.9.37\lib\ServiceStack.OrmLite.SqlServer.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ServiceStack.Redis, Version=3.9.37.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\ServiceStack.Redis.3.9.37\lib\net35\ServiceStack.Redis.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ServiceStack.ServiceInterface, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\ServiceStack.3.9.37\lib\net35\ServiceStack.ServiceInterface.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ServiceStack.Text, Version=3.9.37.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\ServiceStack.Text.3.9.37\lib\net35\ServiceStack.Text.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="JavascriptApiClientService.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
|
||||
<Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
|
||||
<Name>MediaBrowser.Common</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj">
|
||||
<Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
|
||||
<Name>MediaBrowser.Controller</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
|
||||
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
|
||||
<Name>MediaBrowser.Model</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="ApiClient.js" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>xcopy "$(TargetPath)" "$(SolutionDir)\MediaBrowser.ServerApplication\" /y</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
@ -0,0 +1,34 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("MediaBrowser.ApiInteraction.Javascript")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("MediaBrowser.ApiInteraction.Javascript")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2012")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("97f9d4da-d7de-47d9-ae68-06d78679d327")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("2.9.*")]
|
8
MediaBrowser.ApiInteraction.Javascript/packages.config
Normal file
8
MediaBrowser.ApiInteraction.Javascript/packages.config
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="ServiceStack" version="3.9.37" targetFramework="net45" />
|
||||
<package id="ServiceStack.Common" version="3.9.37" targetFramework="net45" />
|
||||
<package id="ServiceStack.OrmLite.SqlServer" version="3.9.37" targetFramework="net45" />
|
||||
<package id="ServiceStack.Redis" version="3.9.37" targetFramework="net45" />
|
||||
<package id="ServiceStack.Text" version="3.9.37" targetFramework="net45" />
|
||||
</packages>
|
@ -1,12 +0,0 @@
|
||||
using System.Net.Http;
|
||||
|
||||
namespace MediaBrowser.ApiInteraction
|
||||
{
|
||||
public class ApiClient : BaseHttpApiClient
|
||||
{
|
||||
public ApiClient(HttpClientHandler handler)
|
||||
: base(handler)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace MediaBrowser.ApiInteraction
|
||||
{
|
||||
public static class DataSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// This is an auto-generated Protobuf Serialization assembly for best performance.
|
||||
/// It is created during the Model project's post-build event.
|
||||
/// This means that this class can currently only handle types within the Model project.
|
||||
/// If we need to, we can always add a param indicating whether or not the model serializer should be used.
|
||||
/// </summary>
|
||||
private static readonly ProtobufModelSerializer ProtobufModelSerializer = new ProtobufModelSerializer();
|
||||
|
||||
public static T DeserializeFromStream<T>(Stream stream, SerializationFormats format)
|
||||
where T : class
|
||||
{
|
||||
if (format == ApiInteraction.SerializationFormats.Protobuf)
|
||||
{
|
||||
return ProtobufModelSerializer.Deserialize(stream, null, typeof(T)) as T;
|
||||
}
|
||||
else if (format == ApiInteraction.SerializationFormats.Jsv)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
else if (format == ApiInteraction.SerializationFormats.Json)
|
||||
{
|
||||
using (StreamReader streamReader = new StreamReader(stream))
|
||||
{
|
||||
using (JsonReader jsonReader = new JsonTextReader(streamReader))
|
||||
{
|
||||
return JsonSerializer.Create(new JsonSerializerSettings()).Deserialize<T>(jsonReader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static object DeserializeFromStream(Stream stream, SerializationFormats format, Type type)
|
||||
{
|
||||
if (format == ApiInteraction.SerializationFormats.Protobuf)
|
||||
{
|
||||
return ProtobufModelSerializer.Deserialize(stream, null, type);
|
||||
}
|
||||
else if (format == ApiInteraction.SerializationFormats.Jsv)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
else if (format == ApiInteraction.SerializationFormats.Json)
|
||||
{
|
||||
using (StreamReader streamReader = new StreamReader(stream))
|
||||
{
|
||||
using (JsonReader jsonReader = new JsonTextReader(streamReader))
|
||||
{
|
||||
return JsonSerializer.Create(new JsonSerializerSettings()).Deserialize(jsonReader, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static void Configure()
|
||||
{
|
||||
}
|
||||
|
||||
public static bool CanDeSerializeJsv
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
76
MediaBrowser.ApiInteraction.Portable/DataSerializer.cs
Normal file
76
MediaBrowser.ApiInteraction.Portable/DataSerializer.cs
Normal file
@ -0,0 +1,76 @@
|
||||
using Newtonsoft.Json;
|
||||
using ProtoBuf;
|
||||
using ProtoBuf.Meta;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace MediaBrowser.ApiInteraction
|
||||
{
|
||||
/// <summary>
|
||||
/// Class DataSerializer
|
||||
/// </summary>
|
||||
public static class DataSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the dynamically created serializer.
|
||||
/// </summary>
|
||||
/// <value>The dynamic serializer.</value>
|
||||
public static TypeModel DynamicSerializer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes an object
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream.</param>
|
||||
/// <param name="format">The format.</param>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
/// <exception cref="System.NotImplementedException"></exception>
|
||||
public static object DeserializeFromStream(Stream stream, SerializationFormats format, Type type)
|
||||
{
|
||||
if (format == SerializationFormats.Protobuf)
|
||||
{
|
||||
if (DynamicSerializer != null)
|
||||
{
|
||||
return DynamicSerializer.Deserialize(stream, null, type);
|
||||
}
|
||||
return Serializer.NonGeneric.Deserialize(type, stream);
|
||||
}
|
||||
if (format == SerializationFormats.Json)
|
||||
{
|
||||
using (var streamReader = new StreamReader(stream))
|
||||
{
|
||||
using (var jsonReader = new JsonTextReader(streamReader))
|
||||
{
|
||||
return JsonSerializer.Create(new JsonSerializerSettings()).Deserialize(jsonReader, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes to json.
|
||||
/// </summary>
|
||||
/// <param name="obj">The obj.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
public static string SerializeToJsonString(object obj)
|
||||
{
|
||||
using (var streamWriter = new StringWriter())
|
||||
{
|
||||
using (var jsonWriter = new JsonTextWriter((streamWriter)))
|
||||
{
|
||||
JsonSerializer.Create(new JsonSerializerSettings()).Serialize(jsonWriter, obj);
|
||||
}
|
||||
return streamWriter.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures this instance.
|
||||
/// </summary>
|
||||
public static void Configure()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{52E0C440-85C0-4A99-ACFE-07C87B2600BE}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MediaBrowser.ApiInteraction.Portable</RootNamespace>
|
||||
<AssemblyName>MediaBrowser.ApiInteraction.Portable</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<TargetFrameworkProfile>Profile104</TargetFrameworkProfile>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||
<RestorePackages>true</RestorePackages>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\MediaBrowser.ApiInteraction\AsyncHttpClient.cs">
|
||||
<Link>AsyncHttpClient.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="DataSerializer.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="..\MediaBrowser.ApiInteraction\BaseApiClient.cs">
|
||||
<Link>BaseApiClient.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.ApiInteraction\ApiClient.cs">
|
||||
<Link>ApiClient.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.ApiInteraction\IAsyncHttpClient.cs">
|
||||
<Link>IAsyncHttpClient.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.ApiInteraction\SerializationFormats.cs">
|
||||
<Link>SerializationFormats.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Threading.Tasks">
|
||||
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.14-rc\lib\portable-net40+sl4+win8+wp71\Microsoft.Threading.Tasks.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Threading.Tasks.Extensions">
|
||||
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.14-rc\lib\portable-net40+sl4+win8+wp71\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>..\packages\Newtonsoft.Json.4.5.11\lib\portable-net40+sl4+wp7+win8\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="protobuf-net">
|
||||
<HintPath>..\packages\protobuf-net.2.0.0.621\lib\portable-sl4+net40+wp7+windows8\protobuf-net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http">
|
||||
<HintPath>..\packages\Microsoft.Net.Http.2.1.3-beta\lib\portable-net40+sl4+win8+wp71\System.Net.Http.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http.Extensions">
|
||||
<HintPath>..\packages\Microsoft.Net.Http.2.1.3-beta\lib\portable-net40+sl4+win8+wp71\System.Net.Http.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http.Primitives">
|
||||
<HintPath>..\packages\Microsoft.Net.Http.2.1.3-beta\lib\portable-net40+sl4+win8+wp71\System.Net.Http.Primitives.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime">
|
||||
<HintPath>..\packages\Microsoft.Bcl.1.0.16-rc\lib\portable-net40+sl4+win8+wp71\System.Runtime.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Threading.Tasks">
|
||||
<HintPath>..\packages\Microsoft.Bcl.1.0.16-rc\lib\portable-net40+sl4+win8+wp71\System.Threading.Tasks.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
|
||||
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
|
||||
<Name>MediaBrowser.Model</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
|
||||
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.0-rc\tools\Microsoft.Bcl.Build.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
@ -1,30 +1,27 @@
|
||||
using System.Resources;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("MediaBrowser.ApiInteraction.Metro")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("MediaBrowser.ApiInteraction.Metro")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2012")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: NeutralResourcesLanguage("en")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("MediaBrowser.ApiInteraction.Portable")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("MediaBrowser.ApiInteraction.Portable")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2012")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: NeutralResourcesLanguage("en")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("2.9.*")]
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user