merge branch master into plugin

pull/2466/head
dkanada 4 years ago
commit bd55bdb4e3

@ -23,7 +23,7 @@ jobs:
NugetPackageName: ${{ Package.value.NugetPackageName }}
AssemblyFileName: ${{ Package.value.AssemblyFileName }}
maxParallel: 2
dependsOn: MainBuild
dependsOn: Build
steps:
- checkout: none

@ -4,15 +4,14 @@ parameters:
DotNetSdkVersion: 3.1.100
jobs:
- job: MainBuild
displayName: Main Build
- job: Build
displayName: Build
strategy:
matrix:
Release:
BuildConfiguration: Release
Debug:
BuildConfiguration: Debug
maxParallel: 2
pool:
vmImage: "${{ parameters.LinuxImage }}"
steps:
@ -22,13 +21,13 @@ jobs:
persistCredentials: true
- task: CmdLine@2
displayName: "Clone Web Client (Master, Release, or Tag)"
displayName: "Clone Web Branch"
condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
inputs:
script: "git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web"
- task: CmdLine@2
displayName: "Clone Web Client (PR)"
displayName: "Clone Web Target"
condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest'))
inputs:
script: "git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web"
@ -37,7 +36,7 @@ jobs:
displayName: "Install Node"
condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
inputs:
versionSpec: "10.x"
versionSpec: "12.x"
- task: CmdLine@2
displayName: "Build Web Client"
@ -69,33 +68,33 @@ jobs:
command: publish
publishWebProjects: false
projects: "${{ parameters.RestoreBuildProjects }}"
arguments: "--configuration $(BuildConfiguration) --output $(build.artifactstagingdirectory)"
arguments: "--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)"
zipAfterPublish: false
- task: PublishPipelineArtifact@0
displayName: "Publish Artifact Naming"
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
inputs:
targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/Emby.Naming.dll"
targetPath: "$(build.ArtifactStagingDirectory)/Jellyfin.Server/Emby.Naming.dll"
artifactName: "Jellyfin.Naming"
- task: PublishPipelineArtifact@0
displayName: "Publish Artifact Controller"
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
inputs:
targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Controller.dll"
targetPath: "$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Controller.dll"
artifactName: "Jellyfin.Controller"
- task: PublishPipelineArtifact@0
displayName: "Publish Artifact Model"
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
inputs:
targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Model.dll"
targetPath: "$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Model.dll"
artifactName: "Jellyfin.Model"
- task: PublishPipelineArtifact@0
displayName: "Publish Artifact Common"
condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release'))
inputs:
targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Common.dll"
targetPath: "$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Common.dll"
artifactName: "Jellyfin.Common"

@ -1,82 +0,0 @@
parameters:
WindowsImage: "windows-latest"
TestProjects: "tests/**/*Tests.csproj"
DotNetSdkVersion: 3.1.100
jobs:
- job: PublishWindows
displayName: Publish Windows
pool:
vmImage: ${{ parameters.WindowsImage }}
steps:
- checkout: self
clean: true
submodules: true
persistCredentials: true
- task: CmdLine@2
displayName: "Clone Web Client (Master, Release, or Tag)"
condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master'), contains(variables['Build.SourceBranch'], 'tag')), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
inputs:
script: "git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web"
- task: CmdLine@2
displayName: "Clone Web Client (PR)"
condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')), in(variables['Build.Reason'], 'PullRequest'))
inputs:
script: "git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web"
- task: NodeTool@0
displayName: "Install Node"
condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
inputs:
versionSpec: "10.x"
- task: CmdLine@2
displayName: "Build Web Client"
condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
inputs:
script: yarn install
workingDirectory: $(Agent.TempDirectory)/jellyfin-web
- task: CopyFiles@2
displayName: "Copy Web Client"
condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion'))
inputs:
sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist
contents: "**"
targetFolder: $(Build.SourcesDirectory)/MediaBrowser.WebDashboard/jellyfin-web
cleanTargetFolder: true
overWrite: true
flattenFolders: false
- task: CmdLine@2
displayName: "Clone UX Repository"
inputs:
script: git clone --depth=1 https://github.com/jellyfin/jellyfin-ux $(Agent.TempDirectory)\jellyfin-ux
- task: PowerShell@2
displayName: "Build NSIS Installer"
inputs:
targetType: "filePath"
filePath: ./deployment/windows/build-jellyfin.ps1
arguments: -InstallFFMPEG -InstallNSSM -MakeNSIS -InstallTrayApp -UXLocation $(Agent.TempDirectory)\jellyfin-ux -InstallLocation $(build.artifactstagingdirectory)
errorActionPreference: "stop"
workingDirectory: $(Build.SourcesDirectory)
- task: CopyFiles@2
displayName: "Copy NSIS Installer"
inputs:
sourceFolder: $(Build.SourcesDirectory)/deployment/windows/
contents: "jellyfin*.exe"
targetFolder: $(System.ArtifactsDirectory)/setup
cleanTargetFolder: true
overWrite: true
flattenFolders: true
- task: PublishPipelineArtifact@0
displayName: "Publish Artifact Setup"
condition: succeeded()
inputs:
targetPath: "$(build.artifactstagingdirectory)/setup"
artifactName: "Jellyfin Server Setup"

@ -27,11 +27,6 @@ jobs:
Windows: "windows-latest"
macOS: "macos-latest"
- template: azure-pipelines-windows.yml
parameters:
WindowsImage: "windows-latest"
TestProjects: $(TestProjects)
- template: azure-pipelines-compat.yml
parameters:
Packages:

@ -0,0 +1,13 @@
---
name: Feature Request
about: Request a new feature
title: ''
labels: feature-request
assignees: ''
---
**PLEASE DO NOT OPEN FEATURE REQUEST ISSUES ON GITHUB**
**Feature requests should be opened on our dedicated [feature request](https://features.jellyfin.org/) hub so they can be appropriately discussed and prioritized.**
However, if you are willing to contribute to the project by adding a new feature yourself, then please ensure that you first review our [documentation](https://docs.jellyfin.org/general/contributing/development.html) on contributing code. Once you have reviewed the documentation, feel free to come back here and open an issue here outlining your proposed approach so that it can be documented, tracked, and discussed by other team members.

1
.gitignore vendored

@ -39,6 +39,7 @@ ProgramData*/
CorePlugins*/
ProgramData-Server*/
ProgramData-UI*/
MediaBrowser.WebDashboard/jellyfin-web/**
#################
## Visual Studio

@ -1,40 +1,133 @@
# Jellyfin Contributors
- [JoshuaBoniface](https://github.com/joshuaboniface)
- [nvllsvm](https://github.com/nvllsvm)
- [JustAMan](https://github.com/JustAMan)
- [97carmine](https://github.com/97carmine)
- [Abbe98](https://github.com/Abbe98)
- [agrenott](https://github.com/agrenott)
- [AndreCarvalho](https://github.com/AndreCarvalho)
- [anthonylavado](https://github.com/anthonylavado)
- [Artiume](https://github.com/Artiume)
- [AThomsen](https://github.com/AThomsen)
- [bilde2910](https://github.com/bilde2910)
- [bfayers](https://github.com/bfayers)
- [BnMcG](https://github.com/BnMcG)
- [Bond-009](https://github.com/Bond-009)
- [brianjmurrell](https://github.com/brianjmurrell)
- [bugfixin](https://github.com/bugfixin)
- [chaosinnovator](https://github.com/chaosinnovator)
- [ckcr4lyf](https://github.com/ckcr4lyf)
- [crankdoofus](https://github.com/crankdoofus)
- [crobibero](https://github.com/crobibero)
- [cromefire](https://github.com/cromefire)
- [cryptobank](https://github.com/cryptobank)
- [cvium](https://github.com/cvium)
- [dannymichel](https://github.com/dannymichel)
- [DaveChild](https://github.com/DaveChild)
- [dcrdev](https://github.com/dcrdev)
- [dhartung](https://github.com/dhartung)
- [dinki](https://github.com/dinki)
- [dkanada](https://github.com/dkanada)
- [dlahoti](https://github.com/dlahoti)
- [dmitrylyzo](https://github.com/dmitrylyzo)
- [DMouse10462](https://github.com/DMouse10462)
- [DrPandemic](https://github.com/DrPandemic)
- [EraYaN](https://github.com/EraYaN)
- [escabe](https://github.com/escabe)
- [excelite](https://github.com/excelite)
- [fasheng](https://github.com/fasheng)
- [ferferga](https://github.com/ferferga)
- [fhriley](https://github.com/fhriley)
- [flemse](https://github.com/flemse)
- [bfayers](https://github.com/bfayers)
- [Bond_009](https://github.com/Bond-009)
- [AnthonyLavado](https://github.com/anthonylavado)
- [sparky8251](https://github.com/sparky8251)
- [LeoVerto](https://github.com/LeoVerto)
- [Froghut](https://github.com/Froghut)
- [fruhnow](https://github.com/fruhnow)
- [geilername](https://github.com/geilername)
- [gnattu](https://github.com/gnattu)
- [grafixeyehero](https://github.com/grafixeyehero)
- [cvium](https://github.com/cvium)
- [wtayl0r](https://github.com/wtayl0r)
- [TtheCreator](https://github.com/Tthecreator)
- [dkanada](https://github.com/dkanada)
- [LogicalPhallacy](https://github.com/LogicalPhallacy/)
- [RazeLighter777](https://github.com/RazeLighter777)
- [WillWill56](https://github.com/WillWill56)
- [h1nk](https://github.com/h1nk)
- [hawken93](https://github.com/hawken93)
- [HelloWorld017](https://github.com/HelloWorld017)
- [jftuga](https://github.com/jftuga)
- [joern-h](https://github.com/joern-h)
- [joshuaboniface](https://github.com/joshuaboniface)
- [JustAMan](https://github.com/JustAMan)
- [justinfenn](https://github.com/justinfenn)
- [KerryRJ](https://github.com/KerryRJ)
- [Larvitar](https://github.com/Larvitar)
- [LeoVerto](https://github.com/LeoVerto)
- [Liggy](https://github.com/Liggy)
- [fruhnow](https://github.com/fruhnow)
- [LogicalPhallacy](https://github.com/LogicalPhallacy)
- [loli10K](https://github.com/loli10K)
- [lostmypillow](https://github.com/lostmypillow)
- [Lynxy](https://github.com/Lynxy)
- [fasheng](https://github.com/fasheng)
- [ploughpuff](https://github.com/ploughpuff)
- [pjeanjean](https://github.com/pjeanjean)
- [DrPandemic](https://github.com/drpandemic)
- [joern-h](https://github.com/joern-h)
- [Khinenw](https://github.com/HelloWorld017)
- [fhriley](https://github.com/fhriley)
- [nevado](https://github.com/nevado)
- [ManfredRichthofen](https://github.com/ManfredRichthofen)
- [Marenz](https://github.com/Marenz)
- [marius-luca-87](https://github.com/marius-luca-87)
- [mark-monteiro](https://github.com/mark-monteiro)
- [ullmie02](https://github.com/ullmie02)
- [geilername](https://github.com/geilername)
- [Matt07211](https://github.com/Matt07211)
- [mcarlton00](https://github.com/mcarlton00)
- [mitchfizz05](https://github.com/mitchfizz05)
- [MrTimscampi](https://github.com/MrTimscampi)
- [n8225](https://github.com/n8225)
- [Narfinger](https://github.com/Narfinger)
- [NathanPickard](https://github.com/NathanPickard)
- [neilsb](https://github.com/neilsb)
- [nevado](https://github.com/nevado)
- [Nickbert7](https://github.com/Nickbert7)
- [nvllsvm](https://github.com/nvllsvm)
- [nyanmisaka](https://github.com/nyanmisaka)
- [oddstr13](https://github.com/oddstr13)
- [petermcneil](https://github.com/petermcneil)
- [Phlogi](https://github.com/Phlogi)
- [pjeanjean](https://github.com/pjeanjean)
- [ploughpuff](https://github.com/ploughpuff)
- [pR0Ps](https://github.com/pR0Ps)
- [PrplHaz4](https://github.com/PrplHaz4)
- [RazeLighter777](https://github.com/RazeLighter777)
- [redSpoutnik](https://github.com/redSpoutnik)
- [ringmatter](https://github.com/ringmatter)
- [ryan-hartzell](https://github.com/ryan-hartzell)
- [s0urcelab](https://github.com/s0urcelab)
- [sachk](https://github.com/sachk)
- [sammyrc34](https://github.com/sammyrc34)
- [samuel9554](https://github.com/samuel9554)
- [scheidleon](https://github.com/scheidleon)
- [sebPomme](https://github.com/sebPomme)
- [SegiH](https://github.com/SegiH)
- [SenorSmartyPants](https://github.com/SenorSmartyPants)
- [shemanaev](https://github.com/shemanaev)
- [skaro13](https://github.com/skaro13)
- [sl1288](https://github.com/sl1288)
- [sorinyo2004](https://github.com/sorinyo2004)
- [sparky8251](https://github.com/sparky8251)
- [stanionascu](https://github.com/stanionascu)
- [stevehayles](https://github.com/stevehayles)
- [SuperSandro2000](https://github.com/SuperSandro2000)
- [tbraeutigam](https://github.com/tbraeutigam)
- [teacupx](https://github.com/teacupx)
- [Terror-Gene](https://github.com/Terror-Gene)
- [ThatNerdyPikachu](https://github.com/ThatNerdyPikachu)
- [ThibaultNocchi](https://github.com/ThibaultNocchi)
- [thornbill](https://github.com/thornbill)
- [ThreeFive-O](https://github.com/ThreeFive-O)
- [TrisMcC](https://github.com/TrisMcC)
- [trumblejoe](https://github.com/trumblejoe)
- [TtheCreator](https://github.com/TtheCreator)
- [twinkybot](https://github.com/twinkybot)
- [Ullmie02](https://github.com/Ullmie02)
- [Unhelpful](https://github.com/Unhelpful)
- [viaregio](https://github.com/viaregio)
- [vitorsemeano](https://github.com/vitorsemeano)
- [voodoos](https://github.com/voodoos)
- [whooo](https://github.com/whooo)
- [WiiPlayer2](https://github.com/WiiPlayer2)
- [WillWill56](https://github.com/WillWill56)
- [wtayl0r](https://github.com/wtayl0r)
- [Wuerfelbecher](https://github.com/Wuerfelbecher)
- [Wunax](https://github.com/Wunax)
- [WWWesten](https://github.com/WWWesten)
- [WX9yMOXWId](https://github.com/WX9yMOXWId)
- [xosdy](https://github.com/xosdy)
- [XVicarious](https://github.com/XVicarious)
- [YouKnowBlom](https://github.com/YouKnowBlom)
# Emby Contributors

@ -3,11 +3,10 @@ ARG FFMPEG_VERSION=latest
FROM node:alpine as web-builder
ARG JELLYFIN_WEB_VERSION=master
RUN apk add curl git \
RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm \
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& cd jellyfin-web-* \
&& yarn install \
&& yarn build \
&& mv dist /dist
FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION}-buster as builder
@ -21,6 +20,13 @@ RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --
FROM jellyfin/ffmpeg:${FFMPEG_VERSION} as ffmpeg
FROM debian:buster-slim
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
ARG DEBIAN_FRONTEND="noninteractive"
# http://stackoverflow.com/questions/48162574/ddg#49462622
ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn
# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support)
ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
COPY --from=ffmpeg /opt/ffmpeg /opt/ffmpeg
COPY --from=builder /jellyfin /jellyfin
COPY --from=web-builder /dist /jellyfin/jellyfin-web
@ -31,16 +37,28 @@ COPY --from=web-builder /dist /jellyfin/jellyfin-web
# mesa-va-drivers: needed for VAAPI
RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y \
libfontconfig1 libgomp1 libva-drm2 mesa-va-drivers openssl ca-certificates \
&& apt-get clean autoclean \
&& apt-get autoremove \
libfontconfig1 \
libgomp1 \
libva-drm2 \
mesa-va-drivers \
openssl \
ca-certificates \
vainfo \
i965-va-driver \
locales \
&& apt-get clean autoclean -y\
&& apt-get autoremove -y\
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /cache /config /media \
&& chmod 777 /cache /config /media \
&& ln -s /opt/ffmpeg/bin/ffmpeg /usr/local/bin \
&& ln -s /opt/ffmpeg/bin/ffprobe /usr/local/bin
&& ln -s /opt/ffmpeg/bin/ffprobe /usr/local/bin \
&& sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
ENV LC_ALL en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
EXPOSE 8096
VOLUME /cache /config /media

@ -7,11 +7,10 @@ ARG DOTNET_VERSION=3.1
FROM node:alpine as web-builder
ARG JELLYFIN_WEB_VERSION=master
RUN apk add curl git \
RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python \
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& cd jellyfin-web-* \
&& yarn install \
&& yarn build \
&& mv dist /dist
@ -27,21 +26,52 @@ RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin"
FROM multiarch/qemu-user-static:x86_64-arm as qemu
FROM arm32v7/debian:buster-slim
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
ARG DEBIAN_FRONTEND="noninteractive"
# http://stackoverflow.com/questions/48162574/ddg#49462622
ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn
# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support)
ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin
RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
libssl-dev ca-certificates \
&& apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg curl && \
curl -ks https://repo.jellyfin.org/debian/jellyfin_team.gpg.key | apt-key add - && \
curl -s https://keyserver.ubuntu.com/pks/lookup?op=get\&search=0x6587ffd6536b8826e88a62547876ae518cbcf2f2 | apt-key add - && \
echo 'deb [arch=armhf] https://repo.jellyfin.org/debian buster main' > /etc/apt/sources.list.d/jellyfin.list && \
echo "deb http://ppa.launchpad.net/ubuntu-raspi2/ppa/ubuntu bionic main">> /etc/apt/sources.list.d/raspbins.list && \
apt-get update && \
apt-get install --no-install-recommends --no-install-suggests -y \
jellyfin-ffmpeg \
libssl-dev \
libfontconfig1 \
libfreetype6 \
libomxil-bellagio0 \
libomxil-bellagio-bin \
libraspberrypi0 \
vainfo \
libva2 \
locales \
&& apt-get remove curl gnupg -y \
&& apt-get clean autoclean -y \
&& apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /cache /config /media \
&& chmod 777 /cache /config /media
&& chmod 777 /cache /config /media \
&& sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
COPY --from=builder /jellyfin /jellyfin
COPY --from=web-builder /dist /jellyfin/jellyfin-web
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
ENV LC_ALL en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
EXPOSE 8096
VOLUME /cache /config /media
ENTRYPOINT ["./jellyfin/jellyfin", \
"--datadir", "/config", \
"--cachedir", "/cache", \
"--ffmpeg", "/usr/bin/ffmpeg"]
"--ffmpeg", "/usr/lib/jellyfin-ffmpeg"]

@ -7,11 +7,10 @@ ARG DOTNET_VERSION=3.1
FROM node:alpine as web-builder
ARG JELLYFIN_WEB_VERSION=master
RUN apk add curl git \
RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python \
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& cd jellyfin-web-* \
&& yarn install \
&& yarn build \
&& mv dist /dist
@ -26,17 +25,38 @@ RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin"
FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
FROM arm64v8/debian:buster-slim
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
ARG DEBIAN_FRONTEND="noninteractive"
# http://stackoverflow.com/questions/48162574/ddg#49462622
ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn
# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support)
ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin
RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
libssl-dev ca-certificates \
RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y \
ffmpeg \
libssl-dev \
ca-certificates \
libfontconfig1 \
libfreetype6 \
libomxil-bellagio0 \
libomxil-bellagio-bin \
locales \
&& apt-get clean autoclean -y \
&& apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /cache /config /media \
&& chmod 777 /cache /config /media
&& chmod 777 /cache /config /media \
&& sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
COPY --from=builder /jellyfin /jellyfin
COPY --from=web-builder /dist /jellyfin/jellyfin-web
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
ENV LC_ALL en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
EXPOSE 8096
VOLUME /cache /config /media

@ -33,7 +33,7 @@ namespace DvdLib.Ifo
continue;
}
var nums = ifo.Name.Split(new [] { '_' }, StringSplitOptions.RemoveEmptyEntries);
var nums = ifo.Name.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries);
if (nums.Length >= 2 && ushort.TryParse(nums[1], out var ifoNumber))
{
ReadVTS(ifoNumber, ifo.FullName);

@ -1,7 +1,7 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text;
using System.Threading.Tasks;
@ -152,6 +152,7 @@ namespace Emby.Dlna.Api
return _resultFactory.GetStaticResult(Request, cacheKey, null, cacheLength, XMLContentType, () => Task.FromResult<Stream>(new MemoryStream(bytes)));
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Get(GetContentDirectory request)
{
var xml = ContentDirectory.GetServiceXml();
@ -159,6 +160,7 @@ namespace Emby.Dlna.Api
return _resultFactory.GetResult(Request, xml, XMLContentType);
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Get(GetMediaReceiverRegistrar request)
{
var xml = MediaReceiverRegistrar.GetServiceXml();
@ -166,6 +168,7 @@ namespace Emby.Dlna.Api
return _resultFactory.GetResult(Request, xml, XMLContentType);
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Get(GetConnnectionManager request)
{
var xml = ConnectionManager.GetServiceXml();
@ -314,31 +317,37 @@ namespace Emby.Dlna.Api
return _resultFactory.GetStaticResult(Request, cacheKey, null, cacheLength, contentType, () => Task.FromResult(_dlnaManager.GetIcon(request.Filename).Stream));
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Subscribe(ProcessContentDirectoryEventRequest request)
{
return ProcessEventRequest(ContentDirectory);
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Subscribe(ProcessConnectionManagerEventRequest request)
{
return ProcessEventRequest(ConnectionManager);
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Subscribe(ProcessMediaReceiverRegistrarEventRequest request)
{
return ProcessEventRequest(MediaReceiverRegistrar);
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Unsubscribe(ProcessContentDirectoryEventRequest request)
{
return ProcessEventRequest(ContentDirectory);
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Unsubscribe(ProcessConnectionManagerEventRequest request)
{
return ProcessEventRequest(ConnectionManager);
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Unsubscribe(ProcessMediaReceiverRegistrarEventRequest request)
{
return ProcessEventRequest(MediaReceiverRegistrar);

@ -1,6 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Net;
@ -53,6 +53,7 @@ namespace Emby.Dlna.Api
_dlnaManager = dlnaManager;
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Get(GetProfileInfos request)
{
return _dlnaManager.GetProfileInfos().ToArray();
@ -63,6 +64,7 @@ namespace Emby.Dlna.Api
return _dlnaManager.GetProfile(request.Id);
}
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "request", Justification = "Required for ServiceStack")]
public object Get(GetDefaultProfile request)
{
return _dlnaManager.GetDefaultProfile();

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna.Common
{

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Globalization;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna.Common
{

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna.Configuration
{

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;
using Emby.Dlna.Configuration;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Threading.Tasks;
using Emby.Dlna.Service;
@ -16,7 +15,11 @@ namespace Emby.Dlna.ConnectionManager
private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
public ConnectionManager(IDlnaManager dlna, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient)
public ConnectionManager(
IDlnaManager dlna,
IServerConfigurationManager config,
ILogger<ConnectionManager> logger,
IHttpClient httpClient)
: base(logger, httpClient)
{
_dlna = dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;
using Emby.Dlna.Common;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Collections.Generic;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;
using Emby.Dlna.Common;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Threading.Tasks;
@ -38,7 +37,7 @@ namespace Emby.Dlna.ContentDirectory
ILibraryManager libraryManager,
IServerConfigurationManager config,
IUserManager userManager,
ILogger logger,
ILogger<ContentDirectory> logger,
IHttpClient httpClient,
ILocalizationManager localization,
IMediaSourceManager mediaSourceManager,

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;
using Emby.Dlna.Common;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@ -79,7 +78,18 @@ namespace Emby.Dlna.ContentDirectory
_profile = profile;
_config = config;
_didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, Logger, mediaEncoder);
_didlBuilder = new DidlBuilder(
profile,
user,
imageProcessor,
serverAddress,
accessToken,
userDataManager,
localization,
mediaSourceManager,
Logger,
mediaEncoder,
libraryManager);
}
/// <inheritdoc />
@ -154,7 +164,7 @@ namespace Emby.Dlna.ContentDirectory
{
var id = sparams["ObjectID"];
var serverItem = GetItemFromObjectId(id, _user);
var serverItem = GetItemFromObjectId(id);
var item = serverItem.Item;
@ -277,7 +287,7 @@ namespace Emby.Dlna.ContentDirectory
DidlBuilder.WriteXmlRootAttributes(_profile, writer);
var serverItem = GetItemFromObjectId(id, _user);
var serverItem = GetItemFromObjectId(id);
var item = serverItem.Item;
@ -294,7 +304,7 @@ namespace Emby.Dlna.ContentDirectory
else
{
var dlnaOptions = _config.GetDlnaConfiguration();
_didlBuilder.WriteItemElement(dlnaOptions, writer, item, _user, null, null, deviceId, filter);
_didlBuilder.WriteItemElement(writer, item, _user, null, null, deviceId, filter);
}
provided++;
@ -321,7 +331,7 @@ namespace Emby.Dlna.ContentDirectory
}
else
{
_didlBuilder.WriteItemElement(dlnaOptions, writer, childItem, _user, item, serverItem.StubType, deviceId, filter);
_didlBuilder.WriteItemElement(writer, childItem, _user, item, serverItem.StubType, deviceId, filter);
}
}
}
@ -388,7 +398,7 @@ namespace Emby.Dlna.ContentDirectory
DidlBuilder.WriteXmlRootAttributes(_profile, writer);
var serverItem = GetItemFromObjectId(sparams["ContainerID"], _user);
var serverItem = GetItemFromObjectId(sparams["ContainerID"]);
var item = serverItem.Item;
@ -407,7 +417,7 @@ namespace Emby.Dlna.ContentDirectory
}
else
{
_didlBuilder.WriteItemElement(dlnaOptions, writer, i, _user, item, serverItem.StubType, deviceId, filter);
_didlBuilder.WriteItemElement(writer, i, _user, item, serverItem.StubType, deviceId, filter);
}
}
@ -513,11 +523,11 @@ namespace Emby.Dlna.ContentDirectory
}
else if (string.Equals(CollectionType.Folders, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
{
return GetFolders(item, user, stubType, sort, startIndex, limit);
return GetFolders(user, startIndex, limit);
}
else if (string.Equals(CollectionType.LiveTv, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
{
return GetLiveTvChannels(item, user, stubType, sort, startIndex, limit);
return GetLiveTvChannels(user, sort, startIndex, limit);
}
}
@ -548,7 +558,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(queryResult);
}
private QueryResult<ServerItem> GetLiveTvChannels(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
private QueryResult<ServerItem> GetLiveTvChannels(User user, SortCriteria sort, int? startIndex, int? limit)
{
var query = new InternalItemsQuery(user)
{
@ -580,7 +590,7 @@ namespace Emby.Dlna.ContentDirectory
if (stubType.HasValue && stubType.Value == StubType.Playlists)
{
return GetMusicPlaylists(item, user, query);
return GetMusicPlaylists(user, query);
}
if (stubType.HasValue && stubType.Value == StubType.Albums)
@ -708,7 +718,7 @@ namespace Emby.Dlna.ContentDirectory
if (stubType.HasValue && stubType.Value == StubType.Collections)
{
return GetMovieCollections(item, user, query);
return GetMovieCollections(user, query);
}
if (stubType.HasValue && stubType.Value == StubType.Favorites)
@ -721,46 +731,42 @@ namespace Emby.Dlna.ContentDirectory
return GetGenres(item, user, query);
}
var list = new List<ServerItem>();
list.Add(new ServerItem(item)
{
StubType = StubType.ContinueWatching
});
list.Add(new ServerItem(item)
var array = new ServerItem[]
{
StubType = StubType.Latest
});
list.Add(new ServerItem(item)
{
StubType = StubType.Movies
});
list.Add(new ServerItem(item)
{
StubType = StubType.Collections
});
list.Add(new ServerItem(item)
{
StubType = StubType.Favorites
});
list.Add(new ServerItem(item)
{
StubType = StubType.Genres
});
new ServerItem(item)
{
StubType = StubType.ContinueWatching
},
new ServerItem(item)
{
StubType = StubType.Latest
},
new ServerItem(item)
{
StubType = StubType.Movies
},
new ServerItem(item)
{
StubType = StubType.Collections
},
new ServerItem(item)
{
StubType = StubType.Favorites
},
new ServerItem(item)
{
StubType = StubType.Genres
}
};
return new QueryResult<ServerItem>
{
Items = list,
TotalRecordCount = list.Count
Items = array,
TotalRecordCount = array.Length
};
}
private QueryResult<ServerItem> GetFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
private QueryResult<ServerItem> GetFolders(User user, int? startIndex, int? limit)
{
var folders = _libraryManager.GetUserRootFolder().GetChildren(user, true)
.OrderBy(i => i.SortName)
@ -793,7 +799,7 @@ namespace Emby.Dlna.ContentDirectory
if (stubType.HasValue && stubType.Value == StubType.NextUp)
{
return GetNextUp(item, user, query);
return GetNextUp(item, query);
}
if (stubType.HasValue && stubType.Value == StubType.Latest)
@ -911,7 +917,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetMovieCollections(BaseItem parent, User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetMovieCollections(User user, InternalItemsQuery query)
{
query.Recursive = true;
//query.Parent = parent;
@ -1106,7 +1112,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(result);
}
private QueryResult<ServerItem> GetMusicPlaylists(BaseItem parent, User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetMusicPlaylists(User user, InternalItemsQuery query)
{
query.Parent = null;
query.IncludeItemTypes = new[] { typeof(Playlist).Name };
@ -1135,7 +1141,7 @@ namespace Emby.Dlna.ContentDirectory
return ToResult(items);
}
private QueryResult<ServerItem> GetNextUp(BaseItem parent, User user, InternalItemsQuery query)
private QueryResult<ServerItem> GetNextUp(BaseItem parent, InternalItemsQuery query)
{
query.OrderBy = Array.Empty<(string, SortOrder)>();
@ -1290,15 +1296,15 @@ namespace Emby.Dlna.ContentDirectory
return result;
}
private ServerItem GetItemFromObjectId(string id, User user)
private ServerItem GetItemFromObjectId(string id)
{
return DidlBuilder.IsIdRoot(id)
? new ServerItem(_libraryManager.GetUserRootFolder())
: ParseItemId(id, user);
: ParseItemId(id);
}
private ServerItem ParseItemId(string id, User user)
private ServerItem ParseItemId(string id)
{
StubType? stubType = null;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;
using Emby.Dlna.Common;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.IO;
using Microsoft.AspNetCore.Http;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Globalization;
@ -46,6 +45,7 @@ namespace Emby.Dlna.Didl
private readonly IMediaSourceManager _mediaSourceManager;
private readonly ILogger _logger;
private readonly IMediaEncoder _mediaEncoder;
private readonly ILibraryManager _libraryManager;
public DidlBuilder(
DeviceProfile profile,
@ -57,7 +57,8 @@ namespace Emby.Dlna.Didl
ILocalizationManager localization,
IMediaSourceManager mediaSourceManager,
ILogger logger,
IMediaEncoder mediaEncoder)
IMediaEncoder mediaEncoder,
ILibraryManager libraryManager)
{
_profile = profile;
_user = user;
@ -69,6 +70,7 @@ namespace Emby.Dlna.Didl
_mediaSourceManager = mediaSourceManager;
_logger = logger;
_mediaEncoder = mediaEncoder;
_libraryManager = libraryManager;
}
public static string NormalizeDlnaMediaUrl(string url)
@ -76,7 +78,7 @@ namespace Emby.Dlna.Didl
return url + "&dlnaheaders=true";
}
public string GetItemDidl(DlnaOptions options, BaseItem item, User user, BaseItem context, string deviceId, Filter filter, StreamInfo streamInfo)
public string GetItemDidl(BaseItem item, User user, BaseItem context, string deviceId, Filter filter, StreamInfo streamInfo)
{
var settings = new XmlWriterSettings
{
@ -101,7 +103,7 @@ namespace Emby.Dlna.Didl
WriteXmlRootAttributes(_profile, writer);
WriteItemElement(options, writer, item, user, context, null, deviceId, filter, streamInfo);
WriteItemElement(writer, item, user, context, null, deviceId, filter, streamInfo);
writer.WriteFullEndElement();
//writer.WriteEndDocument();
@ -128,7 +130,6 @@ namespace Emby.Dlna.Didl
}
public void WriteItemElement(
DlnaOptions options,
XmlWriter writer,
BaseItem item,
User user,
@ -165,25 +166,23 @@ namespace Emby.Dlna.Didl
// refID?
// storeAttribute(itemNode, object, ClassProperties.REF_ID, false);
var hasMediaSources = item as IHasMediaSources;
if (hasMediaSources != null)
if (item is IHasMediaSources)
{
if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
{
AddAudioResource(options, writer, item, deviceId, filter, streamInfo);
AddAudioResource(writer, item, deviceId, filter, streamInfo);
}
else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{
AddVideoResource(options, writer, item, deviceId, filter, streamInfo);
AddVideoResource(writer, item, deviceId, filter, streamInfo);
}
}
AddCover(item, context, null, writer);
AddCover(item, null, writer);
writer.WriteFullEndElement();
}
private void AddVideoResource(DlnaOptions options, XmlWriter writer, BaseItem video, string deviceId, Filter filter, StreamInfo streamInfo = null)
private void AddVideoResource(XmlWriter writer, BaseItem video, string deviceId, Filter filter, StreamInfo streamInfo = null)
{
if (streamInfo == null)
{
@ -227,7 +226,7 @@ namespace Emby.Dlna.Didl
foreach (var contentFeature in contentFeatureList)
{
AddVideoResource(writer, video, deviceId, filter, contentFeature, streamInfo);
AddVideoResource(writer, filter, contentFeature, streamInfo);
}
var subtitleProfiles = streamInfo.GetSubtitleProfiles(_mediaEncoder, false, _serverAddress, _accessToken);
@ -284,7 +283,10 @@ namespace Emby.Dlna.Didl
else
{
writer.WriteStartElement(string.Empty, "res", NS_DIDL);
var protocolInfo = string.Format("http-get:*:text/{0}:*", info.Format.ToLowerInvariant());
var protocolInfo = string.Format(
CultureInfo.InvariantCulture,
"http-get:*:text/{0}:*",
info.Format.ToLowerInvariant());
writer.WriteAttributeString("protocolInfo", protocolInfo);
writer.WriteString(info.Url);
@ -294,7 +296,7 @@ namespace Emby.Dlna.Didl
return true;
}
private void AddVideoResource(XmlWriter writer, BaseItem video, string deviceId, Filter filter, string contentFeatures, StreamInfo streamInfo)
private void AddVideoResource(XmlWriter writer, Filter filter, string contentFeatures, StreamInfo streamInfo)
{
writer.WriteStartElement(string.Empty, "res", NS_DIDL);
@ -336,7 +338,13 @@ namespace Emby.Dlna.Didl
{
if (targetWidth.HasValue && targetHeight.HasValue)
{
writer.WriteAttributeString("resolution", string.Format("{0}x{1}", targetWidth.Value, targetHeight.Value));
writer.WriteAttributeString(
"resolution",
string.Format(
CultureInfo.InvariantCulture,
"{0}x{1}",
targetWidth.Value,
targetHeight.Value));
}
}
@ -370,17 +378,19 @@ namespace Emby.Dlna.Didl
streamInfo.TargetVideoCodecTag,
streamInfo.IsTargetAVC);
var filename = url.Substring(0, url.IndexOf('?'));
var filename = url.Substring(0, url.IndexOf('?', StringComparison.Ordinal));
var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
? MimeTypes.GetMimeType(filename)
: mediaProfile.MimeType;
writer.WriteAttributeString("protocolInfo", string.Format(
"http-get:*:{0}:{1}",
mimeType,
contentFeatures
));
writer.WriteAttributeString(
"protocolInfo",
string.Format(
CultureInfo.InvariantCulture,
"http-get:*:{0}:{1}",
mimeType,
contentFeatures));
writer.WriteString(url);
@ -393,24 +403,24 @@ namespace Emby.Dlna.Didl
{
switch (itemStubType.Value)
{
case StubType.Latest: return _localization.GetLocalizedString("Latest");
case StubType.Playlists: return _localization.GetLocalizedString("Playlists");
case StubType.AlbumArtists: return _localization.GetLocalizedString("HeaderAlbumArtists");
case StubType.Albums: return _localization.GetLocalizedString("Albums");
case StubType.Artists: return _localization.GetLocalizedString("Artists");
case StubType.Songs: return _localization.GetLocalizedString("Songs");
case StubType.Genres: return _localization.GetLocalizedString("Genres");
case StubType.FavoriteAlbums: return _localization.GetLocalizedString("HeaderFavoriteAlbums");
case StubType.FavoriteArtists: return _localization.GetLocalizedString("HeaderFavoriteArtists");
case StubType.FavoriteSongs: return _localization.GetLocalizedString("HeaderFavoriteSongs");
case StubType.Latest: return _localization.GetLocalizedString("Latest");
case StubType.Playlists: return _localization.GetLocalizedString("Playlists");
case StubType.AlbumArtists: return _localization.GetLocalizedString("HeaderAlbumArtists");
case StubType.Albums: return _localization.GetLocalizedString("Albums");
case StubType.Artists: return _localization.GetLocalizedString("Artists");
case StubType.Songs: return _localization.GetLocalizedString("Songs");
case StubType.Genres: return _localization.GetLocalizedString("Genres");
case StubType.FavoriteAlbums: return _localization.GetLocalizedString("HeaderFavoriteAlbums");
case StubType.FavoriteArtists: return _localization.GetLocalizedString("HeaderFavoriteArtists");
case StubType.FavoriteSongs: return _localization.GetLocalizedString("HeaderFavoriteSongs");
case StubType.ContinueWatching: return _localization.GetLocalizedString("HeaderContinueWatching");
case StubType.Movies: return _localization.GetLocalizedString("Movies");
case StubType.Collections: return _localization.GetLocalizedString("Collections");
case StubType.Favorites: return _localization.GetLocalizedString("Favorites");
case StubType.NextUp: return _localization.GetLocalizedString("HeaderNextUp");
case StubType.FavoriteSeries: return _localization.GetLocalizedString("HeaderFavoriteShows");
case StubType.Movies: return _localization.GetLocalizedString("Movies");
case StubType.Collections: return _localization.GetLocalizedString("Collections");
case StubType.Favorites: return _localization.GetLocalizedString("Favorites");
case StubType.NextUp: return _localization.GetLocalizedString("HeaderNextUp");
case StubType.FavoriteSeries: return _localization.GetLocalizedString("HeaderFavoriteShows");
case StubType.FavoriteEpisodes: return _localization.GetLocalizedString("HeaderFavoriteEpisodes");
case StubType.Series: return _localization.GetLocalizedString("Shows");
case StubType.Series: return _localization.GetLocalizedString("Shows");
default: break;
}
}
@ -421,7 +431,10 @@ namespace Emby.Dlna.Didl
if (item.ParentIndexNumber.HasValue && item.ParentIndexNumber.Value == 0
&& season.IndexNumber.HasValue && season.IndexNumber.Value != 0)
{
return string.Format(_localization.GetLocalizedString("ValueSpecialEpisodeName"), item.Name);
return string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("ValueSpecialEpisodeName"),
item.Name);
}
if (item.IndexNumber.HasValue)
@ -436,11 +449,34 @@ namespace Emby.Dlna.Didl
return number + " - " + item.Name;
}
}
else if (item is Episode ep)
{
var parent = ep.GetParent();
var name = parent.Name + " - ";
if (ep.ParentIndexNumber.HasValue)
{
name += "S" + ep.ParentIndexNumber.Value.ToString("00", CultureInfo.InvariantCulture);
}
else if (!item.IndexNumber.HasValue)
{
return name + " - " + item.Name;
}
name += "E" + ep.IndexNumber.Value.ToString("00", CultureInfo.InvariantCulture);
if (ep.IndexNumberEnd.HasValue)
{
name += "-" + ep.IndexNumberEnd.Value.ToString("00", CultureInfo.InvariantCulture);
}
name += " - " + item.Name;
return name;
}
return item.Name;
}
private void AddAudioResource(DlnaOptions options, XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo streamInfo = null)
private void AddAudioResource(XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo streamInfo = null)
{
writer.WriteStartElement(string.Empty, "res", NS_DIDL);
@ -506,7 +542,7 @@ namespace Emby.Dlna.Didl
targetSampleRate,
targetAudioBitDepth);
var filename = url.Substring(0, url.IndexOf('?'));
var filename = url.Substring(0, url.IndexOf('?', StringComparison.Ordinal));
var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
? MimeTypes.GetMimeType(filename)
@ -522,11 +558,13 @@ namespace Emby.Dlna.Didl
streamInfo.RunTimeTicks ?? 0,
streamInfo.TranscodeSeekInfo);
writer.WriteAttributeString("protocolInfo", string.Format(
"http-get:*:{0}:{1}",
mimeType,
contentFeatures
));
writer.WriteAttributeString(
"protocolInfo",
string.Format(
CultureInfo.InvariantCulture,
"http-get:*:{0}:{1}",
mimeType,
contentFeatures));
writer.WriteString(url);
@ -549,7 +587,7 @@ namespace Emby.Dlna.Didl
var clientId = GetClientId(folder, stubType);
if (string.Equals(requestedId, "0"))
if (string.Equals(requestedId, "0", StringComparison.Ordinal))
{
writer.WriteAttributeString("id", "0");
writer.WriteAttributeString("parentID", "-1");
@ -578,7 +616,7 @@ namespace Emby.Dlna.Didl
AddGeneralProperties(folder, stubType, context, writer, filter);
AddCover(folder, context, stubType, writer);
AddCover(folder, stubType, writer);
writer.WriteFullEndElement();
}
@ -611,7 +649,10 @@ namespace Emby.Dlna.Didl
if (playbackPositionTicks > 0)
{
var elementValue = string.Format("BM={0}", Convert.ToInt32(TimeSpan.FromTicks(playbackPositionTicks).TotalSeconds).ToString(_usCulture));
var elementValue = string.Format(
CultureInfo.InvariantCulture,
"BM={0}",
Convert.ToInt32(TimeSpan.FromTicks(playbackPositionTicks).TotalSeconds));
AddValue(writer, "sec", "dcmInfo", elementValue, secAttribute.Value);
}
}
@ -764,37 +805,36 @@ namespace Emby.Dlna.Didl
private void AddPeople(BaseItem item, XmlWriter writer)
{
//var types = new[]
//{
// PersonType.Director,
// PersonType.Writer,
// PersonType.Producer,
// PersonType.Composer,
// "Creator"
//};
//var people = _libraryManager.GetPeople(item);
//var index = 0;
//// Seeing some LG models locking up due content with large lists of people
//// The actual issue might just be due to processing a more metadata than it can handle
//var limit = 6;
if (!item.SupportsPeople)
{
return;
}
//foreach (var actor in people)
//{
// var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
// ?? PersonType.Actor;
var types = new[]
{
PersonType.Director,
PersonType.Writer,
PersonType.Producer,
PersonType.Composer,
"creator"
};
// AddValue(writer, "upnp", type.ToLowerInvariant(), actor.Name, NS_UPNP);
// Seeing some LG models locking up due content with large lists of people
// The actual issue might just be due to processing a more metadata than it can handle
var people = _libraryManager.GetPeople(
new InternalPeopleQuery
{
ItemId = item.Id,
Limit = 6
});
// index++;
foreach (var actor in people)
{
var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
?? PersonType.Actor;
// if (index >= limit)
// {
// break;
// }
//}
AddValue(writer, "upnp", type.ToLowerInvariant(), actor.Name, NS_UPNP);
}
}
private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter)
@ -871,7 +911,7 @@ namespace Emby.Dlna.Didl
}
}
private void AddCover(BaseItem item, BaseItem context, StubType? stubType, XmlWriter writer)
private void AddCover(BaseItem item, StubType? stubType, XmlWriter writer)
{
ImageDownloadInfo imageInfo = GetImageInfo(item);
@ -916,17 +956,8 @@ namespace Emby.Dlna.Didl
}
private void AddEmbeddedImageAsCover(string name, XmlWriter writer)
{
writer.WriteStartElement("upnp", "albumArtURI", NS_UPNP);
writer.WriteAttributeString("dlna", "profileID", NS_DLNA, _profile.AlbumArtPn);
writer.WriteString(_serverAddress + "/Dlna/icons/people480.jpg");
writer.WriteFullEndElement();
writer.WriteElementString("upnp", "icon", NS_UPNP, _serverAddress + "/Dlna/icons/people48.jpg");
}
private void AddImageResElement(BaseItem item,
private void AddImageResElement(
BaseItem item,
XmlWriter writer,
int maxWidth,
int maxHeight,
@ -952,13 +983,17 @@ namespace Emby.Dlna.Didl
var contentFeatures = new ContentFeatureBuilder(_profile)
.BuildImageHeader(format, width, height, imageInfo.IsDirectStream, org_Pn);
writer.WriteAttributeString("protocolInfo", string.Format(
"http-get:*:{0}:{1}",
MimeTypes.GetMimeType("file." + format),
contentFeatures
));
writer.WriteAttributeString(
"protocolInfo",
string.Format(
CultureInfo.InvariantCulture,
"http-get:*:{0}:{1}",
MimeTypes.GetMimeType("file." + format),
contentFeatures));
writer.WriteAttributeString("resolution", string.Format("{0}x{1}", width, height));
writer.WriteAttributeString(
"resolution",
string.Format(CultureInfo.InvariantCulture, "{0}x{1}", width, height));
writer.WriteString(albumartUrlInfo.Url);
@ -1097,7 +1132,9 @@ namespace Emby.Dlna.Didl
private ImageUrlInfo GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, string format)
{
var url = string.Format("{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/0/0",
var url = string.Format(
CultureInfo.InvariantCulture,
"{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/0/0",
_serverAddress,
info.ItemId.ToString("N", CultureInfo.InvariantCulture),
info.Type,

@ -1,8 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using MediaBrowser.Model.Extensions;
namespace Emby.Dlna.Didl
{

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.IO;
@ -54,6 +53,6 @@ namespace Emby.Dlna.Didl
_encoding = encoding;
}
public override Encoding Encoding => (null == _encoding) ? base.Encoding : _encoding;
public override Encoding Encoding => _encoding ?? base.Encoding;
}
}

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Collections.Generic;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Collections.Concurrent;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna
{

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna
{

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna
{

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna
{

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Threading.Tasks;

@ -1,9 +1,8 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Net.Sockets;
using System.Globalization;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using Emby.Dlna.PlayTo;
@ -27,7 +26,7 @@ using MediaBrowser.Model.System;
using Microsoft.Extensions.Logging;
using Rssdp;
using Rssdp.Infrastructure;
using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
namespace Emby.Dlna.Main
{
@ -59,7 +58,9 @@ namespace Emby.Dlna.Main
private ISsdpCommunicationsServer _communicationsServer;
internal IContentDirectory ContentDirectory { get; private set; }
internal IConnectionManager ConnectionManager { get; private set; }
internal IMediaReceiverRegistrar MediaReceiverRegistrar { get; private set; }
public static DlnaEntryPoint Current;
@ -107,7 +108,7 @@ namespace Emby.Dlna.Main
libraryManager,
config,
userManager,
_logger,
loggerFactory.CreateLogger<ContentDirectory.ContentDirectory>(),
httpClient,
localizationManager,
mediaSourceManager,
@ -115,9 +116,16 @@ namespace Emby.Dlna.Main
mediaEncoder,
tvSeriesManager);
ConnectionManager = new ConnectionManager.ConnectionManager(dlnaManager, config, _logger, httpClient);
ConnectionManager = new ConnectionManager.ConnectionManager(
dlnaManager,
config,
loggerFactory.CreateLogger<ConnectionManager.ConnectionManager>(),
httpClient);
MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrar(_logger, httpClient, config);
MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrar(
loggerFactory.CreateLogger<MediaReceiverRegistrar.MediaReceiverRegistrar>(),
httpClient,
config);
Current = this;
}
@ -254,8 +262,8 @@ namespace Emby.Dlna.Main
{
if (address.AddressFamily == AddressFamily.InterNetworkV6)
{
// Not support IPv6 right now
continue;
// Not supporting IPv6 right now
continue;
}
var fullService = "urn:schemas-upnp-org:device:MediaServer:1";

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Collections.Generic;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Threading.Tasks;
using Emby.Dlna.Service;
@ -13,7 +12,10 @@ namespace Emby.Dlna.MediaReceiverRegistrar
{
private readonly IServerConfigurationManager _config;
public MediaReceiverRegistrar(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config)
public MediaReceiverRegistrar(
ILogger<MediaReceiverRegistrar> logger,
IHttpClient httpClient,
IServerConfigurationManager config)
: base(logger, httpClient)
{
_config = config;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;
using Emby.Dlna.Common;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;
using Emby.Dlna.Common;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@ -347,7 +346,12 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service");
}
return new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1));
return new SsdpHttpClient(_httpClient).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
avCommands.BuildPost(command, service.ServiceType, 1),
cancellationToken: cancellationToken);
}
public async Task SetPlay(CancellationToken cancellationToken)
@ -516,8 +520,12 @@ namespace Emby.Dlna.PlayTo
return;
}
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
.ConfigureAwait(false);
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
rendererCommands.BuildPost(command, service.ServiceType),
cancellationToken: cancellationToken).ConfigureAwait(false);
if (result == null || result.Document == null)
{
@ -562,8 +570,12 @@ namespace Emby.Dlna.PlayTo
return;
}
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), true)
.ConfigureAwait(false);
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
rendererCommands.BuildPost(command, service.ServiceType),
cancellationToken: cancellationToken).ConfigureAwait(false);
if (result == null || result.Document == null)
return;
@ -589,8 +601,12 @@ namespace Emby.Dlna.PlayTo
return null;
}
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType), false)
.ConfigureAwait(false);
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
avCommands.BuildPost(command, service.ServiceType),
cancellationToken: cancellationToken).ConfigureAwait(false);
if (result == null || result.Document == null)
{
@ -600,7 +616,7 @@ namespace Emby.Dlna.PlayTo
var transportState =
result.Document.Descendants(uPnpNamespaces.AvTransport + "GetTransportInfoResponse").Select(i => i.Element("CurrentTransportState")).FirstOrDefault(i => i != null);
var transportStateValue = transportState == null ? null : transportState.Value;
var transportStateValue = transportState?.Value;
if (transportStateValue != null
&& Enum.TryParse(transportStateValue, true, out TRANSPORTSTATE state))
@ -627,8 +643,12 @@ namespace Emby.Dlna.PlayTo
var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false)
.ConfigureAwait(false);
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
rendererCommands.BuildPost(command, service.ServiceType),
cancellationToken: cancellationToken).ConfigureAwait(false);
if (result == null || result.Document == null)
{
@ -690,8 +710,12 @@ namespace Emby.Dlna.PlayTo
var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType), false)
.ConfigureAwait(false);
var result = await new SsdpHttpClient(_httpClient).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
rendererCommands.BuildPost(command, service.ServiceType),
cancellationToken: cancellationToken).ConfigureAwait(false);
if (result == null || result.Document == null)
{

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;
using Emby.Dlna.Common;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Collections.Generic;
@ -28,6 +27,8 @@ namespace Emby.Dlna.PlayTo
{
public class PlayToController : ISessionController, IDisposable
{
private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
private Device _device;
private readonly SessionInfo _session;
private readonly ISessionManager _sessionManager;
@ -46,9 +47,10 @@ namespace Emby.Dlna.PlayTo
private readonly string _serverAddress;
private readonly string _accessToken;
public bool IsSessionActive => !_disposed && _device != null;
private readonly List<PlaylistItem> _playlist = new List<PlaylistItem>();
private int _currentPlaylistIndex;
public bool SupportsMediaControl => IsSessionActive;
private bool _disposed;
public PlayToController(
SessionInfo session,
@ -84,18 +86,22 @@ namespace Emby.Dlna.PlayTo
_mediaEncoder = mediaEncoder;
}
public bool IsSessionActive => !_disposed && _device != null;
public bool SupportsMediaControl => IsSessionActive;
public void Init(Device device)
{
_device = device;
_device.OnDeviceUnavailable = OnDeviceUnavailable;
_device.PlaybackStart += _device_PlaybackStart;
_device.PlaybackProgress += _device_PlaybackProgress;
_device.PlaybackStopped += _device_PlaybackStopped;
_device.MediaChanged += _device_MediaChanged;
_device.PlaybackStart += OnDevicePlaybackStart;
_device.PlaybackProgress += OnDevicePlaybackProgress;
_device.PlaybackStopped += OnDevicePlaybackStopped;
_device.MediaChanged += OnDeviceMediaChanged;
_device.Start();
_deviceDiscovery.DeviceLeft += _deviceDiscovery_DeviceLeft;
_deviceDiscovery.DeviceLeft += OnDeviceDiscoveryDeviceLeft;
}
private void OnDeviceUnavailable()
@ -111,7 +117,7 @@ namespace Emby.Dlna.PlayTo
}
}
void _deviceDiscovery_DeviceLeft(object sender, GenericEventArgs<UpnpDeviceInfo> e)
private void OnDeviceDiscoveryDeviceLeft(object sender, GenericEventArgs<UpnpDeviceInfo> e)
{
var info = e.Argument;
@ -126,7 +132,7 @@ namespace Emby.Dlna.PlayTo
}
}
async void _device_MediaChanged(object sender, MediaChangedEventArgs e)
private async void OnDeviceMediaChanged(object sender, MediaChangedEventArgs e)
{
if (_disposed)
{
@ -138,15 +144,15 @@ namespace Emby.Dlna.PlayTo
var streamInfo = StreamParams.ParseFromUrl(e.OldMediaInfo.Url, _libraryManager, _mediaSourceManager);
if (streamInfo.Item != null)
{
var positionTicks = GetProgressPositionTicks(e.OldMediaInfo, streamInfo);
var positionTicks = GetProgressPositionTicks(streamInfo);
ReportPlaybackStopped(e.OldMediaInfo, streamInfo, positionTicks);
ReportPlaybackStopped(streamInfo, positionTicks);
}
streamInfo = StreamParams.ParseFromUrl(e.NewMediaInfo.Url, _libraryManager, _mediaSourceManager);
if (streamInfo.Item == null) return;
var newItemProgress = GetProgressInfo(e.NewMediaInfo, streamInfo);
var newItemProgress = GetProgressInfo(streamInfo);
await _sessionManager.OnPlaybackStart(newItemProgress).ConfigureAwait(false);
}
@ -156,7 +162,7 @@ namespace Emby.Dlna.PlayTo
}
}
async void _device_PlaybackStopped(object sender, PlaybackStoppedEventArgs e)
private async void OnDevicePlaybackStopped(object sender, PlaybackStoppedEventArgs e)
{
if (_disposed)
{
@ -169,9 +175,9 @@ namespace Emby.Dlna.PlayTo
if (streamInfo.Item == null) return;
var positionTicks = GetProgressPositionTicks(e.MediaInfo, streamInfo);
var positionTicks = GetProgressPositionTicks(streamInfo);
ReportPlaybackStopped(e.MediaInfo, streamInfo, positionTicks);
ReportPlaybackStopped(streamInfo, positionTicks);
var mediaSource = await streamInfo.GetMediaSource(CancellationToken.None).ConfigureAwait(false);
@ -195,7 +201,7 @@ namespace Emby.Dlna.PlayTo
}
else
{
Playlist.Clear();
_playlist.Clear();
}
}
catch (Exception ex)
@ -204,7 +210,7 @@ namespace Emby.Dlna.PlayTo
}
}
private async void ReportPlaybackStopped(uBaseObject mediaInfo, StreamParams streamInfo, long? positionTicks)
private async void ReportPlaybackStopped(StreamParams streamInfo, long? positionTicks)
{
try
{
@ -223,7 +229,7 @@ namespace Emby.Dlna.PlayTo
}
}
async void _device_PlaybackStart(object sender, PlaybackStartEventArgs e)
private async void OnDevicePlaybackStart(object sender, PlaybackStartEventArgs e)
{
if (_disposed)
{
@ -236,7 +242,7 @@ namespace Emby.Dlna.PlayTo
if (info.Item != null)
{
var progress = GetProgressInfo(e.MediaInfo, info);
var progress = GetProgressInfo(info);
await _sessionManager.OnPlaybackStart(progress).ConfigureAwait(false);
}
@ -247,7 +253,7 @@ namespace Emby.Dlna.PlayTo
}
}
async void _device_PlaybackProgress(object sender, PlaybackProgressEventArgs e)
private async void OnDevicePlaybackProgress(object sender, PlaybackProgressEventArgs e)
{
if (_disposed)
{
@ -267,7 +273,7 @@ namespace Emby.Dlna.PlayTo
if (info.Item != null)
{
var progress = GetProgressInfo(e.MediaInfo, info);
var progress = GetProgressInfo(info);
await _sessionManager.OnPlaybackProgress(progress).ConfigureAwait(false);
}
@ -278,7 +284,7 @@ namespace Emby.Dlna.PlayTo
}
}
private long? GetProgressPositionTicks(uBaseObject mediaInfo, StreamParams info)
private long? GetProgressPositionTicks(StreamParams info)
{
var ticks = _device.Position.Ticks;
@ -290,13 +296,13 @@ namespace Emby.Dlna.PlayTo
return ticks;
}
private PlaybackStartInfo GetProgressInfo(uBaseObject mediaInfo, StreamParams info)
private PlaybackStartInfo GetProgressInfo(StreamParams info)
{
return new PlaybackStartInfo
{
ItemId = info.ItemId,
SessionId = _session.Id,
PositionTicks = GetProgressPositionTicks(mediaInfo, info),
PositionTicks = GetProgressPositionTicks(info),
IsMuted = _device.IsMuted,
IsPaused = _device.IsPaused,
MediaSourceId = info.MediaSourceId,
@ -311,9 +317,7 @@ namespace Emby.Dlna.PlayTo
};
}
#region SendCommands
public async Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken)
public Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken)
{
_logger.LogDebug("{0} - Received PlayRequest: {1}", this._session.DeviceName, command.PlayCommand);
@ -351,11 +355,12 @@ namespace Emby.Dlna.PlayTo
if (command.PlayCommand == PlayCommand.PlayLast)
{
Playlist.AddRange(playlist);
_playlist.AddRange(playlist);
}
if (command.PlayCommand == PlayCommand.PlayNext)
{
Playlist.AddRange(playlist);
_playlist.AddRange(playlist);
}
if (!command.ControllingUserId.Equals(Guid.Empty))
@ -364,7 +369,7 @@ namespace Emby.Dlna.PlayTo
_session.DeviceName, _session.RemoteEndPoint, user);
}
await PlayItems(playlist).ConfigureAwait(false);
return PlayItems(playlist, cancellationToken);
}
private Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken)
@ -372,7 +377,7 @@ namespace Emby.Dlna.PlayTo
switch (command.Command)
{
case PlaystateCommand.Stop:
Playlist.Clear();
_playlist.Clear();
return _device.SetStop(CancellationToken.None);
case PlaystateCommand.Pause:
@ -388,10 +393,10 @@ namespace Emby.Dlna.PlayTo
return Seek(command.SeekPositionTicks ?? 0);
case PlaystateCommand.NextTrack:
return SetPlaylistIndex(_currentPlaylistIndex + 1);
return SetPlaylistIndex(_currentPlaylistIndex + 1, cancellationToken);
case PlaystateCommand.PreviousTrack:
return SetPlaylistIndex(_currentPlaylistIndex - 1);
return SetPlaylistIndex(_currentPlaylistIndex - 1, cancellationToken);
}
return Task.CompletedTask;
@ -427,14 +432,6 @@ namespace Emby.Dlna.PlayTo
return info.IsDirectStream;
}
#endregion
#region Playlist
private int _currentPlaylistIndex;
private readonly List<PlaylistItem> _playlist = new List<PlaylistItem>();
private List<PlaylistItem> Playlist => _playlist;
private void AddItemFromId(Guid id, List<BaseItem> list)
{
var item = _libraryManager.GetItemById(id);
@ -452,7 +449,7 @@ namespace Emby.Dlna.PlayTo
_dlnaManager.GetDefaultProfile();
var mediaSources = item is IHasMediaSources
? (_mediaSourceManager.GetStaticMediaSources(item, true, user))
? _mediaSourceManager.GetStaticMediaSources(item, true, user)
: new List<MediaSourceInfo>();
var playlistItem = GetPlaylistItem(item, mediaSources, profile, _session.DeviceId, mediaSourceId, audioStreamIndex, subtitleStreamIndex);
@ -460,8 +457,19 @@ namespace Emby.Dlna.PlayTo
playlistItem.StreamUrl = DidlBuilder.NormalizeDlnaMediaUrl(playlistItem.StreamInfo.ToUrl(_serverAddress, _accessToken));
var itemXml = new DidlBuilder(profile, user, _imageProcessor, _serverAddress, _accessToken, _userDataManager, _localization, _mediaSourceManager, _logger, _mediaEncoder)
.GetItemDidl(_config.GetDlnaConfiguration(), item, user, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo);
var itemXml = new DidlBuilder(
profile,
user,
_imageProcessor,
_serverAddress,
_accessToken,
_userDataManager,
_localization,
_mediaSourceManager,
_logger,
_mediaEncoder,
_libraryManager)
.GetItemDidl(item, user, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo);
playlistItem.Didl = itemXml;
@ -571,30 +579,31 @@ namespace Emby.Dlna.PlayTo
/// Plays the items.
/// </summary>
/// <param name="items">The items.</param>
/// <returns></returns>
private async Task<bool> PlayItems(IEnumerable<PlaylistItem> items)
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns><c>true</c> on success.</returns>
private async Task<bool> PlayItems(IEnumerable<PlaylistItem> items, CancellationToken cancellationToken = default)
{
Playlist.Clear();
Playlist.AddRange(items);
_logger.LogDebug("{0} - Playing {1} items", _session.DeviceName, Playlist.Count);
_playlist.Clear();
_playlist.AddRange(items);
_logger.LogDebug("{0} - Playing {1} items", _session.DeviceName, _playlist.Count);
await SetPlaylistIndex(0).ConfigureAwait(false);
await SetPlaylistIndex(0, cancellationToken).ConfigureAwait(false);
return true;
}
private async Task SetPlaylistIndex(int index)
private async Task SetPlaylistIndex(int index, CancellationToken cancellationToken = default)
{
if (index < 0 || index >= Playlist.Count)
if (index < 0 || index >= _playlist.Count)
{
Playlist.Clear();
await _device.SetStop(CancellationToken.None);
_playlist.Clear();
await _device.SetStop(cancellationToken).ConfigureAwait(false);
return;
}
_currentPlaylistIndex = index;
var currentitem = Playlist[index];
var currentitem = _playlist[index];
await _device.SetAvTransport(currentitem.StreamUrl, GetDlnaHeaders(currentitem), currentitem.Didl, CancellationToken.None);
await _device.SetAvTransport(currentitem.StreamUrl, GetDlnaHeaders(currentitem), currentitem.Didl, cancellationToken).ConfigureAwait(false);
var streamInfo = currentitem.StreamInfo;
if (streamInfo.StartPositionTicks > 0 && EnableClientSideSeek(streamInfo))
@ -603,10 +612,7 @@ namespace Emby.Dlna.PlayTo
}
}
#endregion
private bool _disposed;
/// <inheritdoc />
public void Dispose()
{
Dispose(true);
@ -625,19 +631,17 @@ namespace Emby.Dlna.PlayTo
_device.Dispose();
}
_device.PlaybackStart -= _device_PlaybackStart;
_device.PlaybackProgress -= _device_PlaybackProgress;
_device.PlaybackStopped -= _device_PlaybackStopped;
_device.MediaChanged -= _device_MediaChanged;
_deviceDiscovery.DeviceLeft -= _deviceDiscovery_DeviceLeft;
_device.PlaybackStart -= OnDevicePlaybackStart;
_device.PlaybackProgress -= OnDevicePlaybackProgress;
_device.PlaybackStopped -= OnDevicePlaybackStopped;
_device.MediaChanged -= OnDeviceMediaChanged;
_deviceDiscovery.DeviceLeft -= OnDeviceDiscoveryDeviceLeft;
_device.OnDeviceUnavailable = null;
_device = null;
_disposed = true;
}
private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
private Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
{
if (Enum.TryParse(command.Name, true, out GeneralCommandType commandType))
@ -714,7 +718,7 @@ namespace Emby.Dlna.PlayTo
if (info.Item != null)
{
var newPosition = GetProgressPositionTicks(media, info) ?? 0;
var newPosition = GetProgressPositionTicks(info) ?? 0;
var user = !_session.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(_session.UserId) : null;
var newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, newIndex, info.SubtitleStreamIndex);
@ -739,7 +743,7 @@ namespace Emby.Dlna.PlayTo
if (info.Item != null)
{
var newPosition = GetProgressPositionTicks(media, info) ?? 0;
var newPosition = GetProgressPositionTicks(info) ?? 0;
var user = !_session.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(_session.UserId) : null;
var newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, info.AudioStreamIndex, newIndex);
@ -853,8 +857,11 @@ namespace Emby.Dlna.PlayTo
return request;
}
var index = url.IndexOf('?');
if (index == -1) return request;
var index = url.IndexOf('?', StringComparison.Ordinal);
if (index == -1)
{
return request;
}
var query = url.Substring(index + 1);
Dictionary<string, string> values = QueryHelpers.ParseQuery(query).ToDictionary(kv => kv.Key, kv => kv.Value.ToString());

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Globalization;
@ -24,7 +23,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Dlna.PlayTo
{
public class PlayToManager : IDisposable
public sealed class PlayToManager : IDisposable
{
private readonly ILogger _logger;
private readonly ISessionManager _sessionManager;
@ -232,6 +231,7 @@ namespace Emby.Dlna.PlayTo
}
}
/// <inheritdoc />
public void Dispose()
{
_deviceDiscovery.DeviceDiscovered -= OnDeviceDiscoveryDeviceDiscovered;
@ -245,6 +245,9 @@ namespace Emby.Dlna.PlayTo
}
_sessionLock.Dispose();
_disposeCancellationTokenSource.Dispose();
_disposed = true;
}
}

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.IO;
using System.Linq;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Globalization;
@ -33,18 +32,15 @@ namespace Emby.Dlna.PlayTo
DeviceService service,
string command,
string postData,
bool logRequest = true,
string header = null)
string header = null,
CancellationToken cancellationToken = default)
{
var cancellationToken = CancellationToken.None;
var url = NormalizeServiceUrl(baseUrl, service.ControlUrl);
using (var response = await PostSoapDataAsync(
url,
$"\"{service.ServiceType}#{command}\"",
postData,
header,
logRequest,
cancellationToken)
.ConfigureAwait(false))
using (var stream = response.Content)
@ -64,7 +60,7 @@ namespace Emby.Dlna.PlayTo
return serviceUrl;
}
if (!serviceUrl.StartsWith("/"))
if (!serviceUrl.StartsWith("/", StringComparison.Ordinal))
{
serviceUrl = "/" + serviceUrl;
}
@ -128,7 +124,6 @@ namespace Emby.Dlna.PlayTo
string soapAction,
string postData,
string header,
bool logRequest,
CancellationToken cancellationToken)
{
if (soapAction[0] != '\"')

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
namespace Emby.Dlna.PlayTo
{

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Collections.Generic;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Xml.Linq;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Xml.Linq;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Linq;
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using MediaBrowser.Model.Dlna;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Collections.Generic;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Collections.Generic;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using Emby.Dlna.Eventing;
using MediaBrowser.Common.Net;
@ -13,7 +12,7 @@ namespace Emby.Dlna.Service
protected IHttpClient HttpClient;
protected ILogger Logger;
protected BaseService(ILogger logger, IHttpClient httpClient)
protected BaseService(ILogger<BaseService> logger, IHttpClient httpClient)
{
Logger = logger;
HttpClient = httpClient;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.IO;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Collections.Generic;
using System.Text;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Collections.Generic;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System.Xml.Linq;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Globalization;

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.IO;
@ -8,19 +7,12 @@ using Emby.Naming.Common;
namespace Emby.Naming.Audio
{
public class AudioFileParser
public static class AudioFileParser
{
private readonly NamingOptions _options;
public AudioFileParser(NamingOptions options)
{
_options = options;
}
public bool IsAudioFile(string path)
public static bool IsAudioFile(string path, NamingOptions options)
{
var extension = Path.GetExtension(path) ?? string.Empty;
return _options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
return options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
}
}
}

@ -37,7 +37,7 @@ namespace Emby.Naming.AudioBook
/// <value>The type.</value>
public bool IsDirectory { get; set; }
/// <inheritdoc/>
/// <inheritdoc />
public int CompareTo(AudioBookFileInfo other)
{
if (ReferenceEquals(this, other))

@ -1,5 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable SA1600
using System;
using System.Globalization;

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save