commit
0a43814596
@ -0,0 +1,96 @@
|
|||||||
|
parameters:
|
||||||
|
- name: Packages
|
||||||
|
type: object
|
||||||
|
default: {}
|
||||||
|
- name: LinuxImage
|
||||||
|
type: string
|
||||||
|
default: "ubuntu-latest"
|
||||||
|
- name: DotNetSdkVersion
|
||||||
|
type: string
|
||||||
|
default: 3.1.100
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
- job: CompatibilityCheck
|
||||||
|
displayName: Compatibility Check
|
||||||
|
pool:
|
||||||
|
vmImage: "${{ parameters.LinuxImage }}"
|
||||||
|
# only execute for pull requests
|
||||||
|
condition: and(succeeded(), variables['System.PullRequest.PullRequestNumber'])
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
${{ each Package in parameters.Packages }}:
|
||||||
|
${{ Package.key }}:
|
||||||
|
NugetPackageName: ${{ Package.value.NugetPackageName }}
|
||||||
|
AssemblyFileName: ${{ Package.value.AssemblyFileName }}
|
||||||
|
maxParallel: 2
|
||||||
|
dependsOn: MainBuild
|
||||||
|
steps:
|
||||||
|
- checkout: none
|
||||||
|
|
||||||
|
- task: UseDotNet@2
|
||||||
|
displayName: "Update DotNet"
|
||||||
|
inputs:
|
||||||
|
packageType: sdk
|
||||||
|
version: ${{ parameters.DotNetSdkVersion }}
|
||||||
|
|
||||||
|
- task: DownloadPipelineArtifact@2
|
||||||
|
displayName: "Download New Assembly Build Artifact"
|
||||||
|
inputs:
|
||||||
|
source: "current"
|
||||||
|
artifact: "$(NugetPackageName)"
|
||||||
|
path: "$(System.ArtifactsDirectory)/new-artifacts"
|
||||||
|
runVersion: "latest"
|
||||||
|
|
||||||
|
- task: CopyFiles@2
|
||||||
|
displayName: "Copy New Assembly Build Artifact"
|
||||||
|
inputs:
|
||||||
|
sourceFolder: $(System.ArtifactsDirectory)/new-artifacts
|
||||||
|
contents: "**/*.dll"
|
||||||
|
targetFolder: $(System.ArtifactsDirectory)/new-release
|
||||||
|
cleanTargetFolder: true
|
||||||
|
overWrite: true
|
||||||
|
flattenFolders: true
|
||||||
|
|
||||||
|
- task: DownloadPipelineArtifact@2
|
||||||
|
displayName: "Download Reference Assembly Build Artifact"
|
||||||
|
inputs:
|
||||||
|
source: "specific"
|
||||||
|
artifact: "$(NugetPackageName)"
|
||||||
|
path: "$(System.ArtifactsDirectory)/current-artifacts"
|
||||||
|
project: "$(System.TeamProjectId)"
|
||||||
|
pipeline: "$(System.DefinitionId)"
|
||||||
|
runVersion: "latestFromBranch"
|
||||||
|
runBranch: "refs/heads/$(System.PullRequest.TargetBranch)"
|
||||||
|
|
||||||
|
- task: CopyFiles@2
|
||||||
|
displayName: "Copy Reference Assembly Build Artifact"
|
||||||
|
inputs:
|
||||||
|
sourceFolder: $(System.ArtifactsDirectory)/current-artifacts
|
||||||
|
contents: "**/*.dll"
|
||||||
|
targetFolder: $(System.ArtifactsDirectory)/current-release
|
||||||
|
cleanTargetFolder: true
|
||||||
|
overWrite: true
|
||||||
|
flattenFolders: true
|
||||||
|
|
||||||
|
- task: DownloadGitHubRelease@0
|
||||||
|
displayName: "Download ABI Compatibility Check Tool"
|
||||||
|
inputs:
|
||||||
|
connection: Jellyfin Release Download
|
||||||
|
userRepository: EraYaN/dotnet-compatibility
|
||||||
|
defaultVersionType: "latest"
|
||||||
|
itemPattern: "**-ci.zip"
|
||||||
|
downloadPath: "$(System.ArtifactsDirectory)"
|
||||||
|
|
||||||
|
- task: ExtractFiles@1
|
||||||
|
displayName: "Extract ABI Compatibility Check Tool"
|
||||||
|
inputs:
|
||||||
|
archiveFilePatterns: "$(System.ArtifactsDirectory)/*-ci.zip"
|
||||||
|
destinationFolder: $(System.ArtifactsDirectory)/tools
|
||||||
|
cleanDestinationFolder: true
|
||||||
|
|
||||||
|
# The `--warnings-only` switch will swallow the return code and not emit any errors.
|
||||||
|
- task: CmdLine@2
|
||||||
|
displayName: "Execute ABI Compatibility Check Tool"
|
||||||
|
inputs:
|
||||||
|
script: "dotnet tools/CompatibilityCheckerCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines --warnings-only"
|
||||||
|
workingDirectory: $(System.ArtifactsDirectory)
|
@ -0,0 +1,101 @@
|
|||||||
|
parameters:
|
||||||
|
LinuxImage: "ubuntu-latest"
|
||||||
|
RestoreBuildProjects: "Jellyfin.Server/Jellyfin.Server.csproj"
|
||||||
|
DotNetSdkVersion: 3.1.100
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
- job: MainBuild
|
||||||
|
displayName: Main Build
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
Release:
|
||||||
|
BuildConfiguration: Release
|
||||||
|
Debug:
|
||||||
|
BuildConfiguration: Debug
|
||||||
|
maxParallel: 2
|
||||||
|
pool:
|
||||||
|
vmImage: "${{ parameters.LinuxImage }}"
|
||||||
|
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')), 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)"
|
||||||
|
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"
|
||||||
|
|
||||||
|
- 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')), eq(variables['BuildConfiguration'], 'Release'), 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')), eq(variables['BuildConfiguration'], 'Release'), 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')), eq(variables['BuildConfiguration'], 'Release'), 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: UseDotNet@2
|
||||||
|
displayName: "Update DotNet"
|
||||||
|
inputs:
|
||||||
|
packageType: sdk
|
||||||
|
version: ${{ parameters.DotNetSdkVersion }}
|
||||||
|
|
||||||
|
- task: DotNetCoreCLI@2
|
||||||
|
displayName: "Publish Server"
|
||||||
|
inputs:
|
||||||
|
command: publish
|
||||||
|
publishWebProjects: false
|
||||||
|
projects: "${{ parameters.RestoreBuildProjects }}"
|
||||||
|
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"
|
||||||
|
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"
|
||||||
|
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"
|
||||||
|
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"
|
||||||
|
artifactName: "Jellyfin.Common"
|
@ -0,0 +1,65 @@
|
|||||||
|
parameters:
|
||||||
|
- name: ImageNames
|
||||||
|
type: object
|
||||||
|
default:
|
||||||
|
Linux: "ubuntu-latest"
|
||||||
|
Windows: "windows-latest"
|
||||||
|
macOS: "macos-latest"
|
||||||
|
- name: TestProjects
|
||||||
|
type: string
|
||||||
|
default: "tests/**/*Tests.csproj"
|
||||||
|
- name: DotNetSdkVersion
|
||||||
|
type: string
|
||||||
|
default: 3.1.100
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
- job: MainTest
|
||||||
|
displayName: Main Test
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
${{ each imageName in parameters.ImageNames }}:
|
||||||
|
${{ imageName.key }}:
|
||||||
|
ImageName: ${{ imageName.value }}
|
||||||
|
maxParallel: 3
|
||||||
|
pool:
|
||||||
|
vmImage: "$(ImageName)"
|
||||||
|
steps:
|
||||||
|
- checkout: self
|
||||||
|
clean: true
|
||||||
|
submodules: true
|
||||||
|
persistCredentials: false
|
||||||
|
|
||||||
|
- task: UseDotNet@2
|
||||||
|
displayName: "Update DotNet"
|
||||||
|
inputs:
|
||||||
|
packageType: sdk
|
||||||
|
version: ${{ parameters.DotNetSdkVersion }}
|
||||||
|
|
||||||
|
- task: DotNetCoreCLI@2
|
||||||
|
displayName: Run .NET Core CLI tests
|
||||||
|
inputs:
|
||||||
|
command: "test"
|
||||||
|
projects: ${{ parameters.TestProjects }}
|
||||||
|
arguments: '--configuration Release --collect:"XPlat Code Coverage" --settings tests/coverletArgs.runsettings --verbosity minimal "-p:GenerateDocumentationFile=False"'
|
||||||
|
publishTestResults: true
|
||||||
|
testRunTitle: $(Agent.JobName)
|
||||||
|
workingDirectory: "$(Build.SourcesDirectory)"
|
||||||
|
|
||||||
|
- task: Palmmedia.reportgenerator.reportgenerator-build-release-task.reportgenerator@4
|
||||||
|
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging
|
||||||
|
displayName: ReportGenerator (merge)
|
||||||
|
inputs:
|
||||||
|
reports: "$(Agent.TempDirectory)/**/coverage.cobertura.xml"
|
||||||
|
targetdir: "$(Agent.TempDirectory)/merged/"
|
||||||
|
reporttypes: "Cobertura"
|
||||||
|
|
||||||
|
## V2 is already in the repository but it does not work "wrong number of segments" YAML error.
|
||||||
|
- task: PublishCodeCoverageResults@1
|
||||||
|
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging
|
||||||
|
displayName: Publish Code Coverage
|
||||||
|
inputs:
|
||||||
|
codeCoverageTool: "cobertura"
|
||||||
|
#summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' # !!THIS IS FOR V2
|
||||||
|
summaryFileLocation: "$(Agent.TempDirectory)/merged/**.xml"
|
||||||
|
pathToSources: $(Build.SourcesDirectory)
|
||||||
|
failIfCoverageEmpty: true
|
@ -0,0 +1,82 @@
|
|||||||
|
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"
|
@ -1,46 +0,0 @@
|
|||||||
name: Nightly-$(date:yyyyMMdd).$(rev:r)
|
|
||||||
|
|
||||||
variables:
|
|
||||||
- name: Version
|
|
||||||
value: '1.0.0'
|
|
||||||
|
|
||||||
trigger: none
|
|
||||||
pr: none
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
- job: publish_artifacts_nightly
|
|
||||||
displayName: Publish Artifacts Nightly
|
|
||||||
pool:
|
|
||||||
vmImage: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- checkout: none
|
|
||||||
- task: DownloadPipelineArtifact@2
|
|
||||||
displayName: Download the Windows Setup Artifact
|
|
||||||
inputs:
|
|
||||||
source: 'specific' # Options: current, specific
|
|
||||||
artifact: 'Jellyfin Server Setup' # Optional
|
|
||||||
path: '$(System.ArtifactsDirectory)/win-installer'
|
|
||||||
project: '$(System.TeamProjectId)' # Required when source == Specific
|
|
||||||
pipelineId: 1 # Required when source == Specific
|
|
||||||
runVersion: 'latestFromBranch' # Required when source == Specific. Options: latest, latestFromBranch, specific
|
|
||||||
runBranch: 'refs/heads/master' # Required when source == Specific && runVersion == LatestFromBranch
|
|
||||||
|
|
||||||
- task: SSH@0
|
|
||||||
displayName: 'Create Drop directory'
|
|
||||||
inputs:
|
|
||||||
sshEndpoint: 'Jellyfin Build Server'
|
|
||||||
commands: 'mkdir -p /srv/incoming/jellyfin_$(Version)/win-installer && ln -s /srv/incoming/jellyfin_$(Version) /srv/incoming/jellyfin_nightly_azure_upload'
|
|
||||||
|
|
||||||
- task: CopyFilesOverSSH@0
|
|
||||||
displayName: 'Copy the Windows Setup to the Repo'
|
|
||||||
inputs:
|
|
||||||
sshEndpoint: 'Jellyfin Build Server'
|
|
||||||
sourceFolder: '$(System.ArtifactsDirectory)/win-installer'
|
|
||||||
contents: 'jellyfin_*.exe'
|
|
||||||
targetFolder: '/srv/incoming/jellyfin_nightly_azure_upload/win-installer'
|
|
||||||
|
|
||||||
- task: SSH@0
|
|
||||||
displayName: 'Clean up SCP symlink'
|
|
||||||
inputs:
|
|
||||||
sshEndpoint: 'Jellyfin Build Server'
|
|
||||||
commands: 'rm -f /srv/incoming/jellyfin_nightly_azure_upload'
|
|
@ -1,48 +0,0 @@
|
|||||||
name: Release-$(Version)-$(date:yyyyMMdd).$(rev:r)
|
|
||||||
|
|
||||||
variables:
|
|
||||||
- name: Version
|
|
||||||
value: '1.0.0'
|
|
||||||
- name: UsedRunId
|
|
||||||
value: 0
|
|
||||||
|
|
||||||
trigger: none
|
|
||||||
pr: none
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
- job: publish_artifacts_release
|
|
||||||
displayName: Publish Artifacts Release
|
|
||||||
pool:
|
|
||||||
vmImage: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- checkout: none
|
|
||||||
- task: DownloadPipelineArtifact@2
|
|
||||||
displayName: Download the Windows Setup Artifact
|
|
||||||
inputs:
|
|
||||||
source: 'specific' # Options: current, specific
|
|
||||||
artifact: 'Jellyfin Server Setup' # Optional
|
|
||||||
path: '$(System.ArtifactsDirectory)/win-installer'
|
|
||||||
project: '$(System.TeamProjectId)' # Required when source == Specific
|
|
||||||
pipelineId: 1 # Required when source == Specific
|
|
||||||
runVersion: 'specific' # Required when source == Specific. Options: latest, latestFromBranch, specific
|
|
||||||
runId: $(UsedRunId)
|
|
||||||
|
|
||||||
- task: SSH@0
|
|
||||||
displayName: 'Create Drop directory'
|
|
||||||
inputs:
|
|
||||||
sshEndpoint: 'Jellyfin Build Server'
|
|
||||||
commands: 'mkdir -p /srv/incoming/jellyfin_$(Version)/win-installer && ln -s /srv/incoming/jellyfin_$(Version) /srv/incoming/jellyfin_release_azure_upload'
|
|
||||||
|
|
||||||
- task: CopyFilesOverSSH@0
|
|
||||||
displayName: 'Copy the Windows Setup to the Repo'
|
|
||||||
inputs:
|
|
||||||
sshEndpoint: 'Jellyfin Build Server'
|
|
||||||
sourceFolder: '$(System.ArtifactsDirectory)/win-installer'
|
|
||||||
contents: 'jellyfin_*.exe'
|
|
||||||
targetFolder: '/srv/incoming/jellyfin_release_azure_upload/win-installer'
|
|
||||||
|
|
||||||
- task: SSH@0
|
|
||||||
displayName: 'Clean up SCP symlink'
|
|
||||||
inputs:
|
|
||||||
sshEndpoint: 'Jellyfin Build Server'
|
|
||||||
commands: 'rm -f /srv/incoming/jellyfin_release_azure_upload'
|
|
@ -1,26 +0,0 @@
|
|||||||
#pragma warning disable CS1591
|
|
||||||
#pragma warning disable SA1600
|
|
||||||
|
|
||||||
namespace Emby.Naming.Audio
|
|
||||||
{
|
|
||||||
public class MultiPartResult
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the name.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The name.</value>
|
|
||||||
public string Name { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the part.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The part.</value>
|
|
||||||
public string Part { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether this instance is multi part.
|
|
||||||
/// </summary>
|
|
||||||
/// <value><c>true</c> if this instance is multi part; otherwise, <c>false</c>.</value>
|
|
||||||
public bool IsMultiPart { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,89 +1,48 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
#pragma warning disable SA1600
|
#pragma warning disable SA1600
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Emby.Naming.Common;
|
|
||||||
|
|
||||||
namespace Emby.Naming.Video
|
namespace Emby.Naming.Video
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <see href="http://kodi.wiki/view/Advancedsettings.xml#video" />.
|
/// <see href="http://kodi.wiki/view/Advancedsettings.xml#video" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class CleanDateTimeParser
|
public static class CleanDateTimeParser
|
||||||
{
|
{
|
||||||
private readonly NamingOptions _options;
|
public static CleanDateTimeResult Clean(string name, IReadOnlyList<Regex> cleanDateTimeRegexes)
|
||||||
|
|
||||||
public CleanDateTimeParser(NamingOptions options)
|
|
||||||
{
|
|
||||||
_options = options;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CleanDateTimeResult Clean(string name)
|
|
||||||
{
|
|
||||||
var originalName = name;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
var extension = Path.GetExtension(name) ?? string.Empty;
|
CleanDateTimeResult result = new CleanDateTimeResult(name);
|
||||||
// Check supported extensions
|
var len = cleanDateTimeRegexes.Count;
|
||||||
if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)
|
for (int i = 0; i < len; i++)
|
||||||
&& !_options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
|
|
||||||
{
|
{
|
||||||
// Dummy up a file extension because the expressions will fail without one
|
if (TryClean(name, cleanDateTimeRegexes[i], ref result))
|
||||||
// This is tricky because we can't just check Path.GetExtension for empty
|
|
||||||
// If the input is "St. Vincent (2014)", it will produce ". Vincent (2014)" as the extension
|
|
||||||
name += ".mkv";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (ArgumentException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = _options.CleanDateTimeRegexes.Select(i => Clean(name, i))
|
|
||||||
.FirstOrDefault(i => i.HasChanged) ??
|
|
||||||
new CleanDateTimeResult { Name = originalName };
|
|
||||||
|
|
||||||
if (result.HasChanged)
|
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a second pass, running clean string first
|
|
||||||
var cleanStringResult = new CleanStringParser().Clean(name, _options.CleanStringRegexes);
|
|
||||||
|
|
||||||
if (!cleanStringResult.HasChanged)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return _options.CleanDateTimeRegexes.Select(i => Clean(cleanStringResult.Name, i))
|
return result;
|
||||||
.FirstOrDefault(i => i.HasChanged) ??
|
|
||||||
result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CleanDateTimeResult Clean(string name, Regex expression)
|
private static bool TryClean(string name, Regex expression, ref CleanDateTimeResult result)
|
||||||
{
|
{
|
||||||
var result = new CleanDateTimeResult();
|
|
||||||
|
|
||||||
var match = expression.Match(name);
|
var match = expression.Match(name);
|
||||||
|
|
||||||
if (match.Success
|
if (match.Success
|
||||||
&& match.Groups.Count == 4
|
&& match.Groups.Count == 5
|
||||||
&& match.Groups[1].Success
|
&& match.Groups[1].Success
|
||||||
&& match.Groups[2].Success
|
&& match.Groups[2].Success
|
||||||
&& int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year))
|
&& int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year))
|
||||||
{
|
{
|
||||||
name = match.Groups[1].Value;
|
result = new CleanDateTimeResult(match.Groups[1].Value.TrimEnd(), year);
|
||||||
result.Year = year;
|
return true;
|
||||||
result.HasChanged = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Name = name;
|
return false;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,33 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
#pragma warning disable SA1600
|
#pragma warning disable SA1600
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
namespace Emby.Naming.Video
|
namespace Emby.Naming.Video
|
||||||
{
|
{
|
||||||
public class CleanDateTimeResult
|
public readonly struct CleanDateTimeResult
|
||||||
{
|
{
|
||||||
|
public CleanDateTimeResult(string name, int? year)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Year = year;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CleanDateTimeResult(string name)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Year = null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name.
|
/// Gets the name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The name.</value>
|
/// <value>The name.</value>
|
||||||
public string Name { get; set; }
|
public string Name { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the year.
|
/// Gets the year.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The year.</value>
|
/// <value>The year.</value>
|
||||||
public int? Year { get; set; }
|
public int? Year { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether this instance has changed.
|
|
||||||
/// </summary>
|
|
||||||
/// <value><c>true</c> if this instance has changed; otherwise, <c>false</c>.</value>
|
|
||||||
public bool HasChanged { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
#pragma warning disable CS1591
|
|
||||||
#pragma warning disable SA1600
|
|
||||||
|
|
||||||
namespace Emby.Naming.Video
|
|
||||||
{
|
|
||||||
public class CleanStringResult
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the name.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The name.</value>
|
|
||||||
public string Name { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether this instance has changed.
|
|
||||||
/// </summary>
|
|
||||||
/// <value><c>true</c> if this instance has changed; otherwise, <c>false</c>.</value>
|
|
||||||
public bool HasChanged { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
#pragma warning disable CS1591
|
|
||||||
#pragma warning disable SA1600
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Emby.Naming.Video
|
|
||||||
{
|
|
||||||
public class StackResult
|
|
||||||
{
|
|
||||||
public List<FileStack> Stacks { get; set; }
|
|
||||||
|
|
||||||
public StackResult()
|
|
||||||
{
|
|
||||||
Stacks = new List<FileStack>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,128 +0,0 @@
|
|||||||
#pragma warning disable CS1591
|
|
||||||
#pragma warning disable SA1600
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MediaBrowser.Controller;
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
|
||||||
using MediaBrowser.Controller.LiveTv;
|
|
||||||
using MediaBrowser.Controller.Plugins;
|
|
||||||
using MediaBrowser.Controller.Session;
|
|
||||||
using MediaBrowser.Model.LiveTv;
|
|
||||||
using MediaBrowser.Model.Tasks;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.EntryPoints
|
|
||||||
{
|
|
||||||
public class AutomaticRestartEntryPoint : IServerEntryPoint
|
|
||||||
{
|
|
||||||
private readonly IServerApplicationHost _appHost;
|
|
||||||
private readonly ILogger _logger;
|
|
||||||
private readonly ITaskManager _iTaskManager;
|
|
||||||
private readonly ISessionManager _sessionManager;
|
|
||||||
private readonly IServerConfigurationManager _config;
|
|
||||||
private readonly ILiveTvManager _liveTvManager;
|
|
||||||
|
|
||||||
private Timer _timer;
|
|
||||||
|
|
||||||
public AutomaticRestartEntryPoint(IServerApplicationHost appHost, ILogger logger, ITaskManager iTaskManager, ISessionManager sessionManager, IServerConfigurationManager config, ILiveTvManager liveTvManager)
|
|
||||||
{
|
|
||||||
_appHost = appHost;
|
|
||||||
_logger = logger;
|
|
||||||
_iTaskManager = iTaskManager;
|
|
||||||
_sessionManager = sessionManager;
|
|
||||||
_config = config;
|
|
||||||
_liveTvManager = liveTvManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task RunAsync()
|
|
||||||
{
|
|
||||||
if (_appHost.CanSelfRestart)
|
|
||||||
{
|
|
||||||
_appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _appHost_HasPendingRestartChanged(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
DisposeTimer();
|
|
||||||
|
|
||||||
if (_appHost.HasPendingRestart)
|
|
||||||
{
|
|
||||||
_timer = new Timer(TimerCallback, null, TimeSpan.FromMinutes(15), TimeSpan.FromMinutes(15));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void TimerCallback(object state)
|
|
||||||
{
|
|
||||||
if (_config.Configuration.EnableAutomaticRestart)
|
|
||||||
{
|
|
||||||
var isIdle = await IsIdle().ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (isIdle)
|
|
||||||
{
|
|
||||||
DisposeTimer();
|
|
||||||
|
|
||||||
_logger.LogInformation("Automatically restarting the system because it is idle and a restart is required.");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_appHost.Restart();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error restarting server");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<bool> IsIdle()
|
|
||||||
{
|
|
||||||
if (_iTaskManager.ScheduledTasks.Any(i => i.State != TaskState.Idle))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_liveTvManager.Services.Count == 1)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var timers = await _liveTvManager.GetTimers(new TimerQuery(), CancellationToken.None).ConfigureAwait(false);
|
|
||||||
if (timers.Items.Any(i => i.Status == RecordingStatus.InProgress))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error getting timers");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var now = DateTime.UtcNow;
|
|
||||||
|
|
||||||
return !_sessionManager.Sessions.Any(i => (now - i.LastActivityDate).TotalMinutes < 30);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_appHost.HasPendingRestartChanged -= _appHost_HasPendingRestartChanged;
|
|
||||||
|
|
||||||
DisposeTimer();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DisposeTimer()
|
|
||||||
{
|
|
||||||
if (_timer != null)
|
|
||||||
{
|
|
||||||
_timer.Dispose();
|
|
||||||
_timer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,95 @@
|
|||||||
|
{
|
||||||
|
"VersionNumber": "Bersyon {0}",
|
||||||
|
"ValueSpecialEpisodeName": "Espesyal - {0}",
|
||||||
|
"ValueHasBeenAddedToLibrary": "Naidagdag na ang {0} sa iyong media library",
|
||||||
|
"UserStoppedPlayingItemWithValues": "Natapos ni {0} ang {1} sa {2}",
|
||||||
|
"UserStartedPlayingItemWithValues": "Si {0} ay nagplaplay ng {1} sa {2}",
|
||||||
|
"UserPolicyUpdatedWithName": "Ang user policy ay naiupdate para kay {0}",
|
||||||
|
"UserPasswordChangedWithName": "Napalitan na ang password ni {0}",
|
||||||
|
"UserOnlineFromDevice": "Si {0} ay nakakonekta galing sa {1}",
|
||||||
|
"UserOfflineFromDevice": "Si {0} ay nadiskonekta galing sa {1}",
|
||||||
|
"UserLockedOutWithName": "Si {0} ay nalock out",
|
||||||
|
"UserDownloadingItemWithValues": "Nagdadownload si {0} ng {1}",
|
||||||
|
"UserDeletedWithName": "Natanggal na is user {0}",
|
||||||
|
"UserCreatedWithName": "Nagawa na si user {0}",
|
||||||
|
"User": "User",
|
||||||
|
"TvShows": "Pelikula",
|
||||||
|
"System": "Sistema",
|
||||||
|
"Sync": "Pag-sync",
|
||||||
|
"SubtitlesDownloadedForItem": "Naidownload na ang subtitles {0}",
|
||||||
|
"SubtitleDownloadFailureFromForItem": "Hindi naidownload ang subtitles {0} para sa {1}",
|
||||||
|
"StartupEmbyServerIsLoading": "Nagloload ang Jellyfin Server. Sandaling maghintay.",
|
||||||
|
"Songs": "Kanta",
|
||||||
|
"Shows": "Pelikula",
|
||||||
|
"ServerNameNeedsToBeRestarted": "Kailangan irestart ang {0}",
|
||||||
|
"ScheduledTaskStartedWithName": "Nagsimula na ang {0}",
|
||||||
|
"ScheduledTaskFailedWithName": "Hindi gumana and {0}",
|
||||||
|
"ProviderValue": "Ang provider ay {0}",
|
||||||
|
"PluginUpdatedWithName": "Naiupdate na ang {0}",
|
||||||
|
"PluginUninstalledWithName": "Naiuninstall na ang {0}",
|
||||||
|
"PluginInstalledWithName": "Nainstall na ang {0}",
|
||||||
|
"Plugin": "Plugin",
|
||||||
|
"Playlists": "Playlists",
|
||||||
|
"Photos": "Larawan",
|
||||||
|
"NotificationOptionVideoPlaybackStopped": "Huminto na ang pelikula",
|
||||||
|
"NotificationOptionVideoPlayback": "Nagsimula na ang pelikula",
|
||||||
|
"NotificationOptionUserLockedOut": "Nakalock out ang user",
|
||||||
|
"NotificationOptionTaskFailed": "Hindi gumana ang scheduled task",
|
||||||
|
"NotificationOptionServerRestartRequired": "Kailangan irestart ang server",
|
||||||
|
"NotificationOptionPluginUpdateInstalled": "Naiupdate na ang plugin",
|
||||||
|
"NotificationOptionPluginUninstalled": "Naiuninstall na ang plugin",
|
||||||
|
"NotificationOptionPluginInstalled": "Nainstall na ang plugin",
|
||||||
|
"NotificationOptionPluginError": "Hindi gumagana ang plugin",
|
||||||
|
"NotificationOptionNewLibraryContent": "May bagong content na naidagdag",
|
||||||
|
"NotificationOptionInstallationFailed": "Hindi nainstall ng mabuti",
|
||||||
|
"NotificationOptionCameraImageUploaded": "Naiupload na ang picture",
|
||||||
|
"NotificationOptionAudioPlaybackStopped": "Huminto na ang patugtog",
|
||||||
|
"NotificationOptionAudioPlayback": "Nagsimula na ang patugtog",
|
||||||
|
"NotificationOptionApplicationUpdateInstalled": "Naiupdate na ang aplikasyon",
|
||||||
|
"NotificationOptionApplicationUpdateAvailable": "May bagong update ang aplikasyon",
|
||||||
|
"NewVersionIsAvailable": "May bagong version ng Jellyfin Server na pwede idownload.",
|
||||||
|
"NameSeasonUnknown": "Hindi alam ang season",
|
||||||
|
"NameSeasonNumber": "Season {0}",
|
||||||
|
"NameInstallFailed": "Hindi nainstall ang {0}",
|
||||||
|
"MusicVideos": "Music video",
|
||||||
|
"Music": "Kanta",
|
||||||
|
"Movies": "Pelikula",
|
||||||
|
"MixedContent": "Halo-halong content",
|
||||||
|
"MessageServerConfigurationUpdated": "Naiupdate na ang server configuration",
|
||||||
|
"MessageNamedServerConfigurationUpdatedWithValue": "Naiupdate na ang server configuration section {0}",
|
||||||
|
"MessageApplicationUpdatedTo": "Ang Jellyfin Server ay naiupdate to {0}",
|
||||||
|
"MessageApplicationUpdated": "Naiupdate na ang Jellyfin Server",
|
||||||
|
"Latest": "Pinakabago",
|
||||||
|
"LabelRunningTimeValue": "Oras: {0}",
|
||||||
|
"LabelIpAddressValue": "Ang IP Address ay {0}",
|
||||||
|
"ItemRemovedWithName": "Naitanggal ang {0} sa library",
|
||||||
|
"ItemAddedWithName": "Naidagdag ang {0} sa library",
|
||||||
|
"Inherit": "Manahin",
|
||||||
|
"HeaderRecordingGroups": "Pagtatalang Grupo",
|
||||||
|
"HeaderNextUp": "Susunod",
|
||||||
|
"HeaderLiveTV": "Live TV",
|
||||||
|
"HeaderFavoriteSongs": "Paboritong Kanta",
|
||||||
|
"HeaderFavoriteShows": "Paboritong Pelikula",
|
||||||
|
"HeaderFavoriteEpisodes": "Paboritong Episodes",
|
||||||
|
"HeaderFavoriteArtists": "Paboritong Artista",
|
||||||
|
"HeaderFavoriteAlbums": "Paboritong Albums",
|
||||||
|
"HeaderContinueWatching": "Ituloy Manood",
|
||||||
|
"HeaderCameraUploads": "Camera Uploads",
|
||||||
|
"HeaderAlbumArtists": "Artista ng Album",
|
||||||
|
"Genres": "Kategorya",
|
||||||
|
"Folders": "Folders",
|
||||||
|
"Favorites": "Paborito",
|
||||||
|
"FailedLoginAttemptWithUserName": "maling login galing {0}",
|
||||||
|
"DeviceOnlineWithName": "nakakonekta si {0}",
|
||||||
|
"DeviceOfflineWithName": "nadiskonekta si {0}",
|
||||||
|
"Collections": "Koleksyon",
|
||||||
|
"ChapterNameValue": "Kabanata {0}",
|
||||||
|
"Channels": "Channel",
|
||||||
|
"CameraImageUploadedFrom": "May bagong larawan na naupload galing {0}",
|
||||||
|
"Books": "Libro",
|
||||||
|
"AuthenticationSucceededWithUserName": "{0} na patunayan",
|
||||||
|
"Artists": "Artista",
|
||||||
|
"Application": "Aplikasyon",
|
||||||
|
"AppDeviceValues": "Aplikasyon: {0}, Aparato: {1}",
|
||||||
|
"Albums": "Albums"
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
{}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue