Merge pull request #85 from thornbill/downloads-redux

Redesign the downloads page to match the clients page design
This commit is contained in:
Bill Thornton
2022-08-17 11:07:19 -04:00
committed by GitHub
12 changed files with 829 additions and 509 deletions
-80
View File
@@ -1,80 +0,0 @@
/* stylelint-disable docusaurus/copyright-header */
.client__header {
display: flex;
align-items: center;
}
.client__header__start {
flex-grow: 1;
display: flex;
}
.client__header__start h3 {
margin-bottom: 0;
}
.client__header__start__badges {
margin-left: 1rem;
}
.client__footer {
display: flex;
}
.client__platform-icon {
margin-left: 1rem;
}
.client__footer .button {
margin-left: 1rem;
}
.client__footer .button:first-of-type {
margin-left: 0;
}
@media (max-width: 420px) {
.client__header {
flex-direction: column;
text-align: center;
}
.client__platform-icon {
margin-top: 0.5rem;
}
.client__platform-icon:first-of-type {
margin-left: 0;
}
.card__body {
padding-top: 0;
}
}
@media (max-width: 560px) {
.client__header__start {
flex-direction: column;
}
.client__header__start__badges {
margin-top: 0.5rem;
}
.client__header__start__badges:first-of-type {
margin-left: 0;
}
.client__footer {
flex-direction: column;
}
.client__footer .button {
margin: 1rem 0 0;
}
.client__footer .button:first-of-type {
margin-top: 0;
}
}
+24 -32
View File
@@ -1,10 +1,9 @@
import React from 'react';
import DetailsCard from './common/DetailsCard';
import PlatformIcon from './PlatformIcon';
import { Client, ClientType, LicenseType } from '../data/clients';
import './ClientDetails.css';
const ClientTypeBadge = ({ clientType }: { clientType: ClientType }) => {
if (clientType === ClientType.Official) {
return <span className='badge badge--primary margin-right--sm'>Official</span>;
@@ -22,36 +21,29 @@ const LicenseTypeBadge = ({ licenseType }: { licenseType: LicenseType }) => {
};
const ClientDetails = ({ client }: { client: Client }) => (
<div className='card client margin-bottom--md'>
<div className='card__header client__header'>
<div className='client__header__start'>
<h3>{client.name}</h3>
<div className='client__header__start__badges'>
<ClientTypeBadge clientType={client.clientType} />
<LicenseTypeBadge licenseType={client.licenseType} />
</div>
</div>
<div>
{client.platforms.map((platform, index) => (
<PlatformIcon key={`${platform}-${index}`} platform={platform} size={36} className='client__platform-icon' />
))}
</div>
</div>
<div className='card__body padding-top--sm'>{client.description}</div>
<div className='card__footer client__footer'>
{client.secondaryLinks?.map(({ id, url, name }) => (
<a key={id} href={url} className='button button--outline button--primary'>
{name}
</a>
))}
<div style={{ flexGrow: 1 }} />
{client.primaryLinks.map(({ id, url, name }) => (
<a key={id} href={url} className='button button--primary'>
{name}
</a>
))}
</div>
</div>
<DetailsCard
title={client.name}
description={client.description}
badges={
<>
<ClientTypeBadge clientType={client.clientType} />
<LicenseTypeBadge licenseType={client.licenseType} />
</>
}
icons={client.platforms.map((platform, index) => (
<PlatformIcon key={`${platform}-${index}`} platform={platform} size={36} className='client__platform-icon' />
))}
primaryButtons={client.primaryLinks.map(({ id, url, name }) => (
<a key={id} href={url} className='button button--primary'>
{name}
</a>
))}
secondaryButtons={client.secondaryLinks?.map(({ id, url, name }) => (
<a key={id} href={url} className='button button--outline button--primary'>
{name}
</a>
))}
/>
);
export default ClientDetails;
+47 -2
View File
@@ -1,18 +1,29 @@
import {
Amazonfiretv,
Android,
Apple,
Appletv,
Archlinux,
Centos,
Debian,
Discord,
Docker,
Dotnet,
Fedora,
Gentoo,
Ios,
Kodi,
Lg,
Linux,
Roku,
Sailfishos
Sailfishos,
Ubuntu,
Windows
} from '@icons-pack/react-simple-icons';
import clsx from 'clsx';
import React from 'react';
import { Platform } from '../data/clients';
import Platform from '../data/platform';
import Desktop from '../../static/images/icons/monitor.svg';
import Web from '../../static/images/icons/web.svg';
@@ -28,28 +39,56 @@ const PlatformIcon = ({
className = clsx(className, 'fill--white');
switch (platform) {
// TODO: AndroidTV should have a unique icon
case Platform.Android:
case Platform.AndroidTV:
return <Android size={size} className={className} />;
case Platform.Arch:
return <Archlinux size={size} className={className} />;
case Platform.Browser:
return <Web width={size} height={size} className={className} />;
case Platform.CentOS:
return <Centos size={size} className={className} />;
case Platform.Desktop:
return <Desktop width={size} height={size} className={className} />;
case Platform.Debian:
return <Debian size={size} className={className} />;
case Platform.Discord:
return <Discord size={size} className={className} />;
case Platform.Docker:
return <Docker size={size} className={className} />;
case Platform.DotNet:
return <Dotnet size={size} className={className} />;
case Platform.Fedora:
return <Fedora size={size} className={className} />;
case Platform.FireOS:
return <Amazonfiretv size={size} className={className} />;
case Platform.Gentoo:
return <Gentoo size={size} className={className} />;
case Platform.IOS:
return <Ios size={size} className={className} />;
case Platform.Kodi:
return <Kodi size={size} className={className} />;
case Platform.Linux:
return <Linux size={size} className={className} />;
case Platform.MacOS:
return <Apple size={size} className={className} />;
case Platform.Roku:
return <Roku size={size} className={className} />;
@@ -59,9 +98,15 @@ const PlatformIcon = ({
case Platform.TVOS:
return <Appletv size={size} className={className} />;
case Platform.Ubuntu:
return <Ubuntu size={size} className={className} />;
case Platform.WebOS:
return <Lg size={size} className={className} />;
case Platform.Windows:
return <Windows size={size} className={className} />;
default:
return null;
}
+93
View File
@@ -0,0 +1,93 @@
/* stylelint-disable docusaurus/copyright-header */
.details-card__header {
display: flex;
align-items: center;
}
.details-card__header__start {
flex-grow: 1;
display: flex;
}
.details-card__header__start h3 {
margin-bottom: 0;
}
.details-card__header__start__badges {
margin-left: 1rem;
}
.details-card__header__start__badges .badge {
vertical-align: top;
}
.details-card__footer__buttons {
display: flex;
overflow-x: auto;
}
.details-card__footer .row {
margin-top: var(--ifm-card-vertical-spacing);
}
.details-card__footer .row:first-child {
margin-top: 0;
}
.details-card__icons > * {
margin-left: 1rem;
}
.details-card__footer .button {
margin-left: 1rem;
}
.details-card__footer .button:first-child {
margin-left: 0;
}
@media (max-width: 420px) {
.details-card__header {
flex-direction: column;
text-align: center;
}
.details-card__icons > * {
margin-top: 0.5rem;
}
.details-card__icons > *:first-child {
margin-left: 0;
}
.details-card .card__body {
padding-top: 0;
}
}
@media (max-width: 560px) {
.details-card__header__start {
flex-direction: column;
}
.details-card__header__start__badges {
margin-top: 0.5rem;
}
.details-card__header__start__badges:first-of-type {
margin-left: 0;
}
.details-card__footer__buttons {
flex-direction: column;
}
.details-card__footer .button {
margin: 1rem 0 0;
}
.details-card__footer .button:first-child {
margin-top: 0;
}
}
+50
View File
@@ -0,0 +1,50 @@
import React, { FunctionComponent, ReactNode } from 'react';
import './DetailsCard.css';
type DetailsCardProps = {
title: string;
description: ReactNode;
badges?: ReactNode;
icons?: ReactNode;
primaryButtons?: Array<ReactNode>;
secondaryButtons?: Array<ReactNode>;
footerDetails?: ReactNode;
};
const DetailsCard: FunctionComponent<DetailsCardProps> = ({
title,
description,
badges = [],
icons = [],
primaryButtons = [],
secondaryButtons = [],
footerDetails
}) => (
<div className='card details-card margin-bottom--md'>
<div className='card__header details-card__header'>
<div className='details-card__header__start'>
<h3>{title}</h3>
<div className='details-card__header__start__badges'>{badges}</div>
</div>
<div className='details-card__icons'>{icons}</div>
</div>
<div className='card__body padding-top--sm'>{description}</div>
<div className='card__footer details-card__footer container'>
<div className='row'>
<div className='details-card__footer__buttons col'>
{secondaryButtons}
<div style={{ flex: 1 }} />
{primaryButtons}
</div>
</div>
{footerDetails && (
<div className='row'>
<div className='col'>{footerDetails}</div>
</div>
)}
</div>
</div>
);
export default DetailsCard;
@@ -0,0 +1,120 @@
import React, { Dispatch, SetStateAction } from 'react';
import clsx from 'clsx';
import DetailsCard from '../common/DetailsCard';
import { Download, DownloadStatus, Feature } from '../../data/downloads';
import PlatformIcon from '../PlatformIcon';
type DownloadButtonProps = {
name: string;
url?: string;
onClick?(): void;
active?: boolean;
primary?: boolean;
outline?: boolean;
};
const DownloadButton = ({
name,
url,
onClick,
active = false,
primary = true,
outline = false
}: DownloadButtonProps) => {
const className = clsx('button', {
'button--active': active,
'button--primary': primary,
'button--secondary': !primary,
'button--outline': outline
});
if (url) {
return (
<a href={url} className={className}>
{name}
</a>
);
} else if (onClick) {
return (
<button onClick={onClick} className={className}>
{name}
</button>
);
}
return null;
};
const StatusBadge = ({ status }: { status: DownloadStatus }) => {
if (status === DownloadStatus.Official) {
return <span className='badge badge--primary margin-right--sm'>Official</span>;
} else if (status === DownloadStatus.Community) {
return <span className='badge badge--secondary margin-right--sm'>Community</span>;
} else if (status === DownloadStatus.OsPackage) {
return <span className='badge badge--info margin-right--sm'>OS Package</span>;
}
return null;
};
const FfmpegBadge = ({ features }: { features: Array<Feature> }) => {
if (!features.includes(Feature.CustomFfmpeg)) {
return (
<span
className='badge badge--warning margin-right--sm'
title='Jellyfin&#39;s custom build of ffmpeg is not available for this platform. Some features like tonemapping may
not work correctly without this.'
>
Custom ffmpeg Unavailable
</span>
);
}
return null;
};
type DownloadDetailsProps = {
download: Download;
isStableLinks: boolean;
activeButton?: string;
setActiveButton: Dispatch<SetStateAction<string>>;
};
const DownloadDetails = ({ download, isStableLinks, activeButton, setActiveButton }: DownloadDetailsProps) => (
<DetailsCard
title={download.name}
description={download.description}
badges={
<>
<StatusBadge status={download.status} />
<FfmpegBadge features={download.features} />
</>
}
icons={download.platforms.map((platform, index) => (
<PlatformIcon key={`${platform}-${index}`} platform={platform} size={36} />
))}
primaryButtons={
!isStableLinks && download.unstableButtons.length === 0
? ['Unstable Unavailable']
: (isStableLinks ? download.stableButtons : download.unstableButtons).map((button) => (
<DownloadButton
key={button.id}
name={button.name || 'Downloads'}
url={button.url}
onClick={() => {
setActiveButton(button.id);
}}
active={activeButton === button.id}
/>
))
}
secondaryButtons={download.otherButtons.map((button) => (
<DownloadButton key={button.id} name={button.name || 'All Versions'} url={button.url} outline />
))}
footerDetails={
[...download.stableButtons, ...download.unstableButtons, ...download.otherButtons].find(
(button) => button.id === activeButton
)?.details
}
/>
);
export default DownloadDetails;
+17 -11
View File
@@ -5,20 +5,20 @@
* work well for content-centric websites.
*/
html {
html {
font-size: 15px;
line-height: 1.7;
font-weight: 400;
text-rendering: optimizeLegibility;
-moz-font-feature-settings: "liga" on;
font-feature-settings: "liga";
-moz-font-feature-settings: 'liga' on;
font-feature-settings: 'liga';
}
:root {
--ifm-font-color-base: #ffffff;
--ifm-button-color: #ffffff;
--ifm-button-border-radius: 30px;
--ifm-color-primary: #007CA6;
--ifm-color-primary: #007ca6;
--ifm-color-primary-dark: #00749b;
--ifm-color-primary-darker: #006b90;
--ifm-color-primary-darkest: #006385;
@@ -32,7 +32,7 @@
--ifm-card-background-color: #172138;
--ifm-footer-background-color: #333c44;
--ifm-pills-color-background-active: #172138;
--ifm-font-family-base: "Noto Sans", sans-serif;
--ifm-font-family-base: 'Noto Sans', sans-serif;
--ifm-h1-font-size: 2.074em;
--ifm-h2-font-size: 1.728em;
--ifm-footer-link-color: #ffffff;
@@ -48,6 +48,12 @@ h2 {
line-height: 1.35;
}
/* The default text color for this badge is very low contrast */
.badge--info,
.badge--warning {
color: var(--ifm-color-black);
}
.docusaurus-highlight-code-line {
background-color: rgb(72, 77, 91);
display: block;
@@ -60,21 +66,21 @@ h2 {
}
.landing-section {
padding:24pt 0
padding: 24pt 0;
}
.landing-section:first-child {
border-top:1px solid #eee;
border-top: 1px solid #eee;
}
.landing-section:nth-child(even) {
background-color:#172138;
border-top:1px solid #eee;
border-bottom:1px solid #eee
background-color: #172138;
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
}
.landing-section:last-child {
border-bottom:1px solid #eee
border-bottom: 1px solid #eee;
}
.display--flex {
+2 -15
View File
@@ -1,3 +1,5 @@
import Platform from './platform';
export enum ClientType {
Official,
ThirdParty
@@ -14,21 +16,6 @@ export enum LicenseType {
Proprietary
}
export enum Platform {
Android,
AndroidTV,
Browser,
Desktop,
Discord,
FireOS,
IOS,
Kodi,
Roku,
SailfishOS,
TVOS,
WebOS
}
type Link = {
id: string;
name: string;
+347
View File
@@ -0,0 +1,347 @@
import React, { ReactNode } from 'react';
import Platform from './platform';
export enum DownloadStatus {
Official,
Community,
OsPackage
}
export enum Feature {
CustomFfmpeg
}
export enum OsType {
Docker,
Linux,
MacOS,
Windows
}
export type Button = {
id: string;
name?: string;
url?: string;
details?: ReactNode;
};
export type Download = {
id: string;
name: string;
description: ReactNode;
osTypes: Array<OsType>;
status: DownloadStatus;
features: Array<Feature>;
platforms: Array<Platform>;
stableButtons: Array<Button>;
unstableButtons: Array<Button>;
otherButtons: Array<Button>;
};
export const Downloads: Array<Download> = [
{
id: 'debian',
name: 'Debian and Ubuntu',
osTypes: [OsType.Linux],
status: DownloadStatus.Official,
features: [Feature.CustomFfmpeg],
platforms: [Platform.Debian, Platform.Ubuntu],
description: 'Install Jellyfin via our Apt repository or via manual archives (.deb).',
stableButtons: [
{
id: 'debian-stable-button',
name: 'Install Instructions',
details: (
<pre className='margin-bottom--none'>
<code>
{`sudo apt install curl gnupg
curl -fsSL https://repo.jellyfin.org/ubuntu/jellyfin_team.gpg.key | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/jellyfin.gpg
echo "deb [arch=$( dpkg --print-architecture )] https://repo.jellyfin.org/$( awk -F'=' '/^ID=/{ print $NF }' /etc/os-release ) $( awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release ) main" | sudo tee /etc/apt/sources.list.d/jellyfin.list
sudo apt update
sudo apt install jellyfin`}
</code>
</pre>
)
}
],
unstableButtons: [
{
id: 'debian-unstable-button',
name: 'Install Instructions',
details: (
<pre className='margin-bottom--none'>
<code>
{`sudo apt install curl gnupg
curl -fsSL https://repo.jellyfin.org/ubuntu/jellyfin_team.gpg.key | gpg --dearmor -o /etc/apt/trusted.gpg.d/jellyfin.gpg
echo "deb [arch=$( dpkg --print-architecture )] https://repo.jellyfin.org/$( awk -F'=' '/^ID=/{ print $NF }' /etc/os-release ) $( awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release ) main unstable" | sudo tee /etc/apt/sources.list.d/jellyfin.list
sudo apt update
sudo apt install jellyfin`}
</code>
</pre>
)
}
],
otherButtons: [
{
id: 'debian-all-link',
name: 'All Debian Versions',
url: 'https://repo.jellyfin.org/releases/server/debian/versions'
},
{
id: 'ubuntu-all-link',
name: 'All Ubuntu Versions',
url: 'https://repo.jellyfin.org/releases/server/ubuntu/versions'
}
]
},
{
id: 'arch',
name: 'Arch Linux',
osTypes: [OsType.Linux],
status: DownloadStatus.Community,
features: [Feature.CustomFfmpeg],
platforms: [Platform.Arch],
description: 'Install Jellyfin via the Arch User Repository.',
stableButtons: [
{
id: 'arch-stable-button',
name: 'Install Instructions',
details: (
<>
<pre>
<code>
{`git clone https://aur.archlinux.org/jellyfin.git
cd jellyfin
makepkg -si`}
</code>
</pre>
<p>
<b>Note:</b> The third command should give you output similar to{' '}
<code>deb [arch=(architecture)] https://repo.jellyfin.org/(distribution) (release) main</code>. We support{' '}
<code>amd64</code>, <code>armhf</code>, and <code>arm64</code> for architectures, <code>debian</code> and{' '}
<code>ubuntu</code> for distributions, <code>buster</code> and <code>bullseye</code> for Debian releases
and <code>bionic</code>, <code>focal</code>, <code>impish</code> and <code>jammy</code> for Ubuntu
releases. If you see something different in your output, you might need to manually modify it. Use the
closest equivalent Debian or Ubuntu version instead.
</p>
<p className='margin-bottom--none'>
Once installed, Jellyfin will be running as a service. Manage it with{' '}
<code>{'sudo systemctl {action} jellyfin.service'}</code> or{' '}
<code>{'sudo service jellyfin {action}'}</code>.
</p>
</>
)
}
],
unstableButtons: [
{
id: 'arch-unstable-button',
name: 'Install Instructions',
details: (
<>
<pre>
<code>
{`git clone https://aur.archlinux.org/jellyfin-git.git
cd jellyfin-git
makepkg -si`}
</code>
</pre>
<p>
<b>Note:</b> The third command should give you output similar to{' '}
<code>deb [arch=(architecture)] https://repo.jellyfin.org/(distribution) (release) main</code>. We support{' '}
<code>amd64</code>, <code>armhf</code>, and <code>arm64</code> for architectures, <code>debian</code> and{' '}
<code>ubuntu</code> for distributions, <code>buster</code> and <code>bullseye</code> for Debian releases
and <code>bionic</code>, <code>focal</code>, <code>impish</code> and <code>jammy</code> for Ubuntu
releases. If you see something different in your output, you might need to manually modify it. Use the
closest equivalent Debian or Ubuntu version instead.
</p>
<p>
<b>Note:</b> Both the <code>main</code> and <code>unstable</code> are needed as the{' '}
<code>jellyfin-ffmpeg</code> package is only in the <code>main</code> component.
</p>
<p className='margin-bottom--none'>
Once installed, Jellyfin will be running as a service. Manage it with{' '}
<code>{'sudo systemctl {action} jellyfin.service'}</code> or{' '}
<code>{'sudo service jellyfin {action}'}</code>.
</p>
</>
)
}
],
otherButtons: [
{
id: 'arch-aur-link',
name: 'AUR',
url: 'https://aur.archlinux.org/packages/?K=jellyfin'
}
]
},
{
id: 'fedora',
name: 'Fedora and CentOS',
osTypes: [OsType.Linux],
status: DownloadStatus.Community,
features: [],
platforms: [Platform.Fedora, Platform.CentOS],
description: 'RPM archives for both Fedora and CentOS are provided.',
stableButtons: [
{
id: 'fedora-stable-link',
name: 'Fedora Downloads',
url: 'https://repo.jellyfin.org/releases/server/fedora/stable'
},
{
id: 'centos-stable-link',
name: 'CentOS Downloads',
url: 'https://repo.jellyfin.org/releases/server/centos/stable'
}
],
unstableButtons: [
{
id: 'fedora-unstable-link',
name: 'Fedora Downloads',
url: 'https://repo.jellyfin.org/releases/server/fedora/unstable'
},
{
id: 'centos-unstable-link',
name: 'CentOS Downloads',
url: 'https://repo.jellyfin.org/releases/server/centos/unstable'
}
],
otherButtons: [
{
id: 'fedora-all-link',
name: 'All Fedora Versions',
url: 'https://repo.jellyfin.org/releases/server/fedora/versions'
},
{
id: 'centos-all-link',
name: 'All CentOS Versions',
url: 'https://repo.jellyfin.org/releases/server/centos/versions'
}
]
},
{
id: 'gentoo',
name: 'Gentoo Linux',
osTypes: [OsType.Linux],
status: DownloadStatus.OsPackage,
features: [],
platforms: [Platform.Gentoo],
description: 'Install Jellyfin via the Gentoo Repository.',
stableButtons: [
{
id: 'gentoo-stable-button',
name: 'Install Instructions',
details: (
<>
<pre>
<code>emerge www-apps/jellyfin</code>
</pre>
<p className='margin-bottom--none'>
Once installed, Jellyfin will be running as a service. Manage it with{' '}
<code>{'sudo systemctl {action} jellyfin.service'}</code> or{' '}
<code>{'sudo rc-service jellyfin {action}'}</code>
</p>
</>
)
}
],
unstableButtons: [],
otherButtons: []
},
{
id: 'generic-linux',
name: 'Generic Linux',
osTypes: [OsType.Linux],
status: DownloadStatus.Official,
features: [],
platforms: [Platform.Linux],
description: 'Linux self-contained binary TAR archives (.tar.gz) are provided.',
stableButtons: [{ id: 'linux-stable-link', url: 'https://repo.jellyfin.org/releases/server/linux/stable' }],
unstableButtons: [{ id: 'linux-unstable-link', url: 'https://repo.jellyfin.org/releases/server/linux/unstable' }],
otherButtons: [{ id: 'linux-all-link', url: 'https://repo.jellyfin.org/releases/server/linux/versions' }]
},
{
id: 'windows',
name: 'Windows',
osTypes: [OsType.Windows],
status: DownloadStatus.Official,
features: [Feature.CustomFfmpeg],
platforms: [Platform.Windows],
description: 'Both installers (.exe) and manual ZIP archives (.zip) are provided.',
stableButtons: [{ id: 'windows-stable-link', url: 'https://repo.jellyfin.org/releases/server/windows/stable' }],
unstableButtons: [
{ id: 'windows-unstable-link', url: 'https://repo.jellyfin.org/releases/server/windows/unstable' }
],
otherButtons: [{ id: 'windows-all-link', url: 'https://repo.jellyfin.org/releases/server/windows/versions' }]
},
{
id: 'macos',
name: 'MacOS',
osTypes: [OsType.MacOS],
status: DownloadStatus.Official,
features: [Feature.CustomFfmpeg],
platforms: [Platform.MacOS],
description: 'Both installers (.dmg) and manual ZIP archives (.tar.gz) are provided.',
stableButtons: [{ id: 'macos-stable-link', url: 'https://repo.jellyfin.org/releases/server/macos/stable' }],
unstableButtons: [{ id: 'macos-unstable-link', url: 'https://repo.jellyfin.org/releases/server/macos/unstable' }],
otherButtons: [{ id: 'macos-all-link', url: 'https://repo.jellyfin.org/releases/server/macos/versions' }]
},
{
id: 'docker',
name: 'Docker',
osTypes: [OsType.Docker],
status: DownloadStatus.Official,
features: [Feature.CustomFfmpeg],
platforms: [Platform.Docker],
description: (
<>
Run Jellyfin in Docker. Example commands store data in <code>/srv/jellyfin</code> and assume your media is
stored under <code>/media</code>.
</>
),
stableButtons: [
{
id: 'docker-stable-button',
name: 'Install Instructions',
details: (
<pre className='margin-bottom--none'>
<code>{`docker pull jellyfin/jellyfin:latest
mkdir -p /srv/jellyfin/{config,cache}
docker run -d -v /srv/jellyfin/config:/config -v /srv/jellyfin/cache:/cache -v /media:/media --net=host jellyfin/jellyfin:latest`}</code>
</pre>
)
}
],
unstableButtons: [
{
id: 'docker-unstable-button',
name: 'Install Instructions',
details: (
<pre className='margin-bottom--none'>
<code>{`docker pull jellyfin/jellyfin:unstable
mkdir -p /srv/jellyfin/{config,cache}
docker run -d -v /srv/jellyfin/config:/config -v /srv/jellyfin/cache:/cache -v /media:/media --net=host jellyfin/jellyfin:unstable`}</code>
</pre>
)
}
],
otherButtons: [{ id: 'docker-hub-link', name: 'Docker Hub', url: 'https://hub.docker.com/r/jellyfin/jellyfin/' }]
},
{
id: 'portable',
name: 'Portable',
osTypes: [OsType.Linux, OsType.MacOS, OsType.Windows],
status: DownloadStatus.Official,
features: [],
platforms: [Platform.DotNet],
description: 'The portable version can be run on any system with a .NET Core runtime.',
stableButtons: [{ id: 'portable-stable-link', url: 'https://repo.jellyfin.org/releases/server/portable/stable' }],
unstableButtons: [
{ id: 'portable-unstable-link', url: 'https://repo.jellyfin.org/releases/server/portable/unstable' }
],
otherButtons: [{ id: 'portable-all-link', url: 'https://repo.jellyfin.org/releases/server/portable/versions' }]
}
];
+30
View File
@@ -0,0 +1,30 @@
const enum Platform {
// Client platforms
Android,
AndroidTV,
Browser,
Desktop,
Discord,
FireOS,
IOS,
Kodi,
Roku,
SailfishOS,
TVOS,
WebOS,
// Server platforms
Arch,
CentOS,
Debian,
Docker,
DotNet,
Fedora,
Gentoo,
Linux,
MacOS,
Ubuntu,
Windows
}
export default Platform;
+2 -1
View File
@@ -3,7 +3,8 @@ import React, { useState } from 'react';
import ClientDetails from '../components/ClientDetails';
import Pill from '../components/Pill';
import { Clients, DeviceType, Platform } from '../data/clients';
import { Clients, DeviceType } from '../data/clients';
import Platform from '../data/platform';
type ClientFilter = {
recommended: boolean;
+97 -368
View File
@@ -1,388 +1,117 @@
import React, { useState } from 'react';
import Layout from '@theme/Layout';
import clsx from 'clsx';
import Hero from '../components/Hero';
import Admonition from '@theme-original/Admonition';
enum InstallOption {
DockerStable,
DockerUnstable,
DebianStable,
DebianUnstable,
ArchStable,
ArchUnstable
}
import Pill from '../components/Pill';
import DownloadDetails from '../components/downloads/DownloadDetails';
import { Downloads, OsType } from '../data/downloads';
const InstallInstructions = {
Docker: {
Stable: `docker pull jellyfin/jellyfin:latest
mkdir -p /srv/jellyfin/{config,cache}
docker run -d -v /srv/jellyfin/config:/config -v /srv/jellyfin/cache:/cache -v /media:/media --net=host jellyfin/jellyfin:latest`,
Unstable: `docker pull jellyfin/jellyfin:unstable
mkdir -p /srv/jellyfin/{config,cache}
docker run -d -v /srv/jellyfin/config:/config -v /srv/jellyfin/cache:/cache -v /media:/media --net=host jellyfin/jellyfin:unstable`
},
Debian: {
Stable: `sudo apt install curl gnupg
curl -fsSL https://repo.jellyfin.org/ubuntu/jellyfin_team.gpg.key | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/jellyfin.gpg
echo "deb [arch=$( dpkg --print-architecture )] https://repo.jellyfin.org/$( awk -F'=' '/^ID=/{ print $NF }' /etc/os-release ) $( awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release ) main" | sudo tee /etc/apt/sources.list.d/jellyfin.list
sudo apt update
sudo apt install jellyfin`,
Unstable: `sudo apt install curl gnupg
curl -fsSL https://repo.jellyfin.org/ubuntu/jellyfin_team.gpg.key | gpg --dearmor -o /etc/apt/trusted.gpg.d/jellyfin.gpg
echo "deb [arch=$( dpkg --print-architecture )] https://repo.jellyfin.org/$( awk -F'=' '/^ID=/{ print $NF }' /etc/os-release ) $( awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release ) main unstable" | sudo tee /etc/apt/sources.list.d/jellyfin.list
sudo apt update
sudo apt install jellyfin`
},
Arch: {
Stable: `git clone https://aur.archlinux.org/jellyfin.git
cd jellyfin
makepkg -si`,
Unstable: `git clone https://aur.archlinux.org/jellyfin-git.git
cd jellyfin-git
makepkg -si`
}
};
export default function DownloadsPage() {
const [osType, setOsType] = useState<OsType>(OsType.Linux);
const [isStableLinks, setIsStableLinks] = useState<boolean>(true);
const [isStableHelpVisible, setIsStableHelpVisible] = useState<boolean>(false);
const [activeButton, setActiveButton] = useState<string>();
export default function Downloads() {
const [installOption, setInstallOption] = useState<InstallOption>(null);
return (
<Layout title='Downloads'>
<Hero title='Downloads'>
<p className='hero__text'>You can download the latest releases of Jellyfin Server below!</p>
</Hero>
<h1 className='text--center'>Downloads</h1>
<main className='margin-vert--lg'>
<section className='container'>
<h2>Stable or Unstable?</h2>
<p>
Generally, if you&apos;re a new user or value stability use the stable version. It won&apos;t change very
often. If you want to help test the latest improvements and features and can handle some occasional
breakage, use the unstable version. Always back up your existing configuration before testing unstable
releases.
</p>
<div className='row margin-bottom--md'>
<div className='col'>
<ul className='pills margin-bottom--none' style={{ overflowX: 'auto' }}>
<Pill
active={osType === OsType.Linux}
onClick={() => {
setOsType(OsType.Linux);
}}
>
Linux
</Pill>
<Pill
active={osType === OsType.Windows}
onClick={() => {
setOsType(OsType.Windows);
}}
>
Windows
</Pill>
<Pill
active={osType === OsType.MacOS}
onClick={() => {
setOsType(OsType.MacOS);
}}
>
macOS
</Pill>
<Pill
active={osType === OsType.Docker}
onClick={() => {
setOsType(OsType.Docker);
}}
>
Docker
</Pill>
</ul>
</div>
<h3>
Docker
<span className='badge badge--success margin-left--sm'>Official</span>
</h3>
<p>
Run Jellyfin in Docker. Example commands store data in <code>/srv/jellyfin</code> and assume your media is
stored under <code>/media</code>.
</p>
<p>
<button
className={clsx('button button--primary margin-right--md margin-bottom--md', {
'button--active': installOption === InstallOption.DockerStable
})}
onClick={() => {
setInstallOption(InstallOption.DockerStable);
}}
>
Stable
</button>
<button
className={clsx('button button--secondary margin-right--md margin-bottom--md', {
'button--active': installOption === InstallOption.DockerUnstable
})}
onClick={() => {
setInstallOption(InstallOption.DockerUnstable);
}}
>
Unstable
</button>
<a
href='https://hub.docker.com/r/jellyfin/jellyfin/'
className='button button--outline button--primary margin-bottom--md'
>
Docker Hub
</a>
{installOption === InstallOption.DockerStable && (
<pre>
<code>{InstallInstructions.Docker.Stable}</code>
</pre>
)}
{installOption === InstallOption.DockerUnstable && (
<pre>
<code>{InstallInstructions.Docker.Unstable}</code>
</pre>
)}
</p>
<div className='col' style={{ textAlign: 'right' }}>
<button
className='button button--link'
onClick={() => {
setIsStableHelpVisible(!isStableHelpVisible);
}}
style={{
verticalAlign: 'baseline'
}}
>
Help?
</button>
<h3>
Debian and Ubuntu
<span className='badge badge--success margin-left--sm'>Official</span>
</h3>
<p>Install Jellyfin via our Apt repository or via manual archives (.deb).</p>
<p>
<button
className={clsx('button button--primary margin-right--md margin-bottom--md', {
'button--active': installOption === InstallOption.DebianStable
})}
onClick={() => {
setInstallOption(InstallOption.DebianStable);
}}
>
Stable
</button>
<button
className={clsx('button button--secondary margin-right--md margin-bottom--md', {
'button--active': installOption === InstallOption.DebianUnstable
})}
onClick={() => {
setInstallOption(InstallOption.DebianUnstable);
}}
>
Unstable
</button>
<a
href='https://repo.jellyfin.org/releases/server/debian/versions'
className='button button--outline button--primary margin-right--md margin-bottom--md'
>
All Debian Versions
</a>
<a
href='https://repo.jellyfin.org/releases/server/ubuntu/versions'
className='button button--outline button--primary margin-bottom--md'
>
All Ubuntu Versions
</a>
{installOption === InstallOption.DebianStable && (
<pre>
<code>{InstallInstructions.Debian.Stable}</code>
</pre>
)}
{installOption === InstallOption.DebianUnstable && (
<pre>
<code>{InstallInstructions.Debian.Unstable}</code>
</pre>
)}
</p>
<ul className='pills margin-bottom--none' style={{ display: 'inline-flex' }}>
<Pill
active={!isStableLinks}
onClick={() => {
setIsStableLinks(false);
setActiveButton(null);
}}
>
Unstable
</Pill>
<Pill
active={isStableLinks}
onClick={() => {
setIsStableLinks(true);
setActiveButton(null);
}}
>
Stable
</Pill>
</ul>
</div>
</div>
<h3>
Arch Linux
<span className='badge badge--info margin-left--sm'>Community</span>
</h3>
<p>Install Jellyfin via the Arch User Repository.</p>
<p>
<button
className={clsx('button button--primary margin-right--md margin-bottom--md', {
'button--active': installOption === InstallOption.ArchStable
})}
onClick={() => {
setInstallOption(InstallOption.ArchStable);
}}
>
Stable
</button>
<button
className={clsx('button button--secondary margin-right--md margin-bottom--md', {
'button--active': installOption === InstallOption.ArchUnstable
})}
onClick={() => {
setInstallOption(InstallOption.ArchUnstable);
}}
>
Unstable
</button>
<a
href='https://aur.archlinux.org/packages/?K=jellyfin'
className='button button--outline button--primary margin-bottom--md'
>
AUR
</a>
{installOption === InstallOption.ArchStable && (
<pre>
<code>{InstallInstructions.Arch.Stable}</code>
</pre>
)}
{installOption === InstallOption.ArchUnstable && (
<pre>
<code>{InstallInstructions.Arch.Unstable}</code>
</pre>
)}
</p>
{installOption === InstallOption.ArchStable && (
<>
{isStableHelpVisible && (
<Admonition type='tip' title='Stable or Unstable?'>
<p>
<b>Note:</b> The third command should give you output similar to{' '}
<code>deb [arch=(architecture)] https://repo.jellyfin.org/(distribution) (release) main</code>. We
support <code>amd64</code>, <code>armhf</code>, and <code>arm64</code> for architectures,{' '}
<code>debian</code> and <code>ubuntu</code> for distributions, <code>buster</code> and{' '}
<code>bullseye</code> for Debian releases and <code>bionic</code>, <code>focal</code>,{' '}
<code>impish</code> and <code>jammy</code> for Ubuntu releases. If you see something different in your
output, you might need to manually modify it. Use the closest equivalent Debian or Ubuntu version
instead.
Generally, if you&apos;re a new user or value stability use the stable version. It won&apos;t change
very often. If you want to help test the latest improvements and features and can handle some occasional
breakage, use the unstable version. Always back up your existing configuration before testing unstable
releases.
</p>
<p>
Once installed, Jellyfin will be running as a service. Manage it with{' '}
<code>{'sudo systemctl {action} jellyfin.service'}</code> or{' '}
<code>{'sudo service jellyfin {action}'}</code>.
</p>
</>
)}
{installOption === InstallOption.ArchUnstable && (
<>
<p>
<b>Note:</b> The third command should give you output similar to{' '}
<code>deb [arch=(architecture)] https://repo.jellyfin.org/(distribution) (release) main</code>. We
support <code>amd64</code>, <code>armhf</code>, and <code>arm64</code> for architectures,{' '}
<code>debian</code> and <code>ubuntu</code> for distributions, <code>buster</code> and{' '}
<code>bullseye</code> for Debian releases and <code>bionic</code>, <code>focal</code>,{' '}
<code>impish</code> and <code>jammy</code> for Ubuntu releases. If you see something different in your
output, you might need to manually modify it. Use the closest equivalent Debian or Ubuntu version
instead.
</p>
<p>
<b>Note:</b> Both the <code>main</code> and <code>unstable</code> are needed as the{' '}
<code>jellyfin-ffmpeg</code> package is only in the <code>main</code> component.
</p>
<p>
Once installed, Jellyfin will be running as a service. Manage it with{' '}
<code>{'sudo systemctl {action} jellyfin.service'}</code> or{' '}
<code>{'sudo service jellyfin {action}'}</code>.
</p>
</>
</Admonition>
)}
<h3>
Fedora and CentOS
<span className='badge badge--info margin-left--sm'>Community</span>
</h3>
<p>RPM archives for both Fedora and CentOS are provided.</p>
<p>
<a
href='https://repo.jellyfin.org/releases/server/fedora'
className='button button--primary margin-right--md margin-bottom--md'
>
Stable Fedora
</a>
<a
href='https://repo.jellyfin.org/releases/server/centos'
className='button button--primary margin-right--md margin-bottom--md'
>
Stable CentOS
</a>
<a
href='https://repo.jellyfin.org/releases/server/fedora/versions'
className='button button--outline button--primary margin-right--md margin-bottom--md'
>
All Fedora Versions
</a>
<a
href='https://repo.jellyfin.org/releases/server/centos/versions'
className='button button--outline button--primary margin-bottom--md'
>
All CentOS Versions
</a>
</p>
<h3>
Generic Linux
<span className='badge badge--success margin-left--sm'>Official</span>
</h3>
<p>Linux self-contained binary TAR archives (.tar.gz) are provided.</p>
<p>
<a
href='https://repo.jellyfin.org/releases/server/linux/stable'
className='button button--primary margin-right--md margin-bottom--md'
>
Stable
</a>
<a
href='https://repo.jellyfin.org/releases/server/linux/unstable'
className='button button--secondary margin-right--md margin-bottom--md'
>
Unstable
</a>
<a
href='https://repo.jellyfin.org/releases/server/linux/versions'
className='button button--outline button--primary margin-bottom--md'
>
All Versions
</a>
</p>
<h3>
MacOS
<span className='badge badge--success margin-left--sm'>Official</span>
</h3>
<p>Both installers (.dmg) and manual ZIP archives (.tar.gz) are provided.</p>
<p>
<a
href='https://repo.jellyfin.org/releases/server/macos/stable'
className='button button--primary margin-right--md margin-bottom--md'
>
Stable
</a>
<a
href='https://repo.jellyfin.org/releases/server/macos/unstable'
className='button button--secondary margin-right--md margin-bottom--md'
>
Unstable
</a>
<a
href='https://repo.jellyfin.org/releases/server/macos/versions'
className='button button--outline button--primary margin-bottom--md'
>
All Versions
</a>
</p>
<h3>
Windows
<span className='badge badge--success margin-left--sm'>Official</span>
</h3>
<p>Both installers (.exe) and manual ZIP archives (.zip) are provided.</p>
<p>
When using the installer, please ensure you <i>fully uninstall</i> any ZIP archive versions you may have
installed, or you may get duplicate services.
</p>
<p>
<a
href='https://repo.jellyfin.org/releases/server/windows/stable'
className='button button--primary margin-right--md margin-bottom--md'
>
Stable
</a>
<a
href='https://repo.jellyfin.org/releases/server/windows/unstable'
className='button button--secondary margin-right--md margin-bottom--md'
>
Unstable
</a>
<a
href='https://repo.jellyfin.org/releases/server/windows/versions'
className='button button--outline button--primary margin-bottom--md'
>
All Versions
</a>
</p>
<h3>
Portable
<span className='badge badge--success margin-left--sm'>Official</span>
</h3>
<p>The portable version can be run on any system with a .NET Core runtime.</p>
<p>
<a
href='https://repo.jellyfin.org/releases/server/portable/stable'
className='button button--primary margin-right--md margin-bottom--md'
>
Stable
</a>
<a
href='https://repo.jellyfin.org/releases/server/portable/unstable'
className='button button--secondary margin-right--md margin-bottom--md'
>
Unstable
</a>
<a
href='https://repo.jellyfin.org/releases/server/portable/versions'
className='button button--outline button--primary margin-bottom--md'
>
All Versions
</a>
</p>
{Downloads.filter((download) => download.osTypes.includes(osType)).map((download) => (
<DownloadDetails
key={download.id}
download={download}
isStableLinks={isStableLinks}
activeButton={activeButton}
setActiveButton={setActiveButton}
/>
))}
</section>
</main>
</Layout>