diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e80b79216..b2eaa00d7 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1,4 @@ blog/ @jellyfin/core docs/general/about.md @jellyfin/core docs/general/community-standards.md @joshuaboniface +content/developers/ @nielsvanvelzen diff --git a/content/developers/docs/_sidebar.js b/content/developers/docs/_sidebar.js new file mode 100644 index 000000000..8726089f0 --- /dev/null +++ b/content/developers/docs/_sidebar.js @@ -0,0 +1,6 @@ +module.exports = { + developers: [ + { type: 'autogenerated', dirName: '.' }, + { type: 'link', label: 'API Documentation', href: 'https://api.jellyfin.org' } + ] +}; diff --git a/content/developers/docs/api/_category_.yml b/content/developers/docs/api/_category_.yml new file mode 100644 index 000000000..e8f9489e1 --- /dev/null +++ b/content/developers/docs/api/_category_.yml @@ -0,0 +1,2 @@ +label: 'HTTP API' +position: 2 diff --git a/content/developers/docs/api/authorization.md b/content/developers/docs/api/authorization.md new file mode 100644 index 000000000..2990de29e --- /dev/null +++ b/content/developers/docs/api/authorization.md @@ -0,0 +1,72 @@ +--- +title: Authorization +sidebar_position: 2 +--- + +# Authorization + +To start using the Jellyfin API, authorization is probably the first thing you'll need to do. Jellyfin's authorization options can be a bit confusing because there are a lot of deprecated options. + +Generally there are three ways to authenticate: no authorization, user authorization with an access token or authorization with an API key. The first way is easy, just do nothing. But most often you'll need to use either the access token or API key. + +## Sending authorization values + +There are multiple methods for transmitting authorization values, however, some are outdated and scheduled to be removed. +It's recommend to use the `Authorization` header. If header auth isn't an option, the token should be sent through the `ApiKey` query parameter. + +| Type | Name | Method | Deprecated | +| ------ | ---------------------- | ---------- | ---------- | +| Header | `Authorization` | Schema | No | +| Query | `ApiKey` | Token only | No | +| Query | `api_key` | Token only | **yes** | +| Header | `X-Emby-Token` | Token only | **yes** | +| Header | `X-MediaBrowser-Token` | Token only | **yes** | +| Header | `X-Emby-Authorization` | Schema | **yes** | + +Avoid sending multiple tokens in one request as it's uncertain which value will be used. Deprecated options might be removed in future server updates. + +## The Jellyfin authorization scheme + +The [Authorization header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization) uses the format `Authorization: `. The Jellyfin scheme is named `MediaBrowser` and it uses named values separated by commas. There is no specific order for parameters. All keys are case sensitive and only allow alphanumeric characters. Unknown keys are ignored by the server. Values must be wrapped in double quotes (`"`) and should use [url encoding](https://en.wikipedia.org/wiki/URL_encoding). + +```txt +MediaBrowser key="value", key2="value2", key3="value3" +``` + +### Parameters + +| Key | Description | +| -------- | -------------------------------------------------- | +| Token | The access token or API key | +| Client | The name of the client | +| Version | The version of the client | +| DeviceId | A unique id for the device generated by the client | +| Device | The device name | + +The token parameter is required to use authenticated endpoints. The client and version properties are used to identify the client in the dashboard. + +#### Device identifiers + +When it comes to device identifiers in the Jellyfin API, it's important to understand the nuances involved. While generating a random string for the `deviceId` might seem like a straightforward solution, there are certain limitations to consider. Currently, the server permits only a single access token for each `deviceId`. This means that you cannot have multiple users signed into your client with a single randomized string. To work around this limitation, you'll need to use a unique identifier for each combination of a device-specific identifier and user-specific identifier. + +Since it's often not possible to know the user identifier before signing in at least once, we recommend including the username as user-specific identifier. It is advisable to hash the username because it is user-input that could include double quotes (escaping the header value format) or special characters that your HTTP library might not allow. + +### Examples + +Here are a couple of examples for the authorization header: + +- Authenticate with API key + + ```http + Authorization: MediaBrowser Token="8ac3a7abaff943ba9adea7f8754da7f8" + ``` + +- Authenticate with access token + + ```http + Authorization: MediaBrowser Token="0381cf931f9e42d79fb9c89f729167df", Client="Android TV", Device="Nvidia Shield", DeviceId="ZQ9YQHHrUzk24vV", Version="0.15.3" + ``` + +## The ApiKey query parameter + +Use the `ApiKey` query parameter when the `Authorization` header can't be used. The value of the query parameter is the access token or API key. Avoid using this option if possible. Never use the `ApiKey` query parameter and the `Authorization` header at the same time. diff --git a/docs/general/contributing/branding.md b/content/developers/docs/branding.md similarity index 98% rename from docs/general/contributing/branding.md rename to content/developers/docs/branding.md index d90bdf3de..385737394 100644 --- a/docs/general/contributing/branding.md +++ b/content/developers/docs/branding.md @@ -1,6 +1,7 @@ --- uid: contrib-branding -title: Branding +title: Jellyfin branding +sidebar_position: 3 --- # Branding diff --git a/content/developers/docs/contributing/_category_.yml b/content/developers/docs/contributing/_category_.yml new file mode 100644 index 000000000..26714ed4a --- /dev/null +++ b/content/developers/docs/contributing/_category_.yml @@ -0,0 +1,2 @@ +label: 'Contributing' +position: 4 diff --git a/docs/general/contributing/development.md b/content/developers/docs/contributing/development.md similarity index 100% rename from docs/general/contributing/development.md rename to content/developers/docs/contributing/development.md diff --git a/docs/general/contributing/index.md b/content/developers/docs/contributing/index.md similarity index 100% rename from docs/general/contributing/index.md rename to content/developers/docs/contributing/index.md diff --git a/docs/general/contributing/issues.md b/content/developers/docs/contributing/issues.md similarity index 100% rename from docs/general/contributing/issues.md rename to content/developers/docs/contributing/issues.md diff --git a/docs/general/contributing/release-procedure.md b/content/developers/docs/contributing/release-procedure.md similarity index 100% rename from docs/general/contributing/release-procedure.md rename to content/developers/docs/contributing/release-procedure.md diff --git a/docs/general/contributing/source-tree.md b/content/developers/docs/contributing/source-tree.md similarity index 100% rename from docs/general/contributing/source-tree.md rename to content/developers/docs/contributing/source-tree.md diff --git a/content/developers/docs/style-guides/_category_.yml b/content/developers/docs/style-guides/_category_.yml new file mode 100644 index 000000000..77ee45643 --- /dev/null +++ b/content/developers/docs/style-guides/_category_.yml @@ -0,0 +1,2 @@ +label: 'Style guides' +position: 5 diff --git a/docs/general/style-guides/index.md b/content/developers/docs/style-guides/index.md similarity index 100% rename from docs/general/style-guides/index.md rename to content/developers/docs/style-guides/index.md diff --git a/docs/general/style-guides/javascript.md b/content/developers/docs/style-guides/javascript.md similarity index 100% rename from docs/general/style-guides/javascript.md rename to content/developers/docs/style-guides/javascript.md diff --git a/content/developers/pages/index.scss b/content/developers/pages/index.scss new file mode 100644 index 000000000..0812805f9 --- /dev/null +++ b/content/developers/pages/index.scss @@ -0,0 +1,16 @@ +.card--dev-index { + h2 { + margin: 10px 20px 0 20px; + } + + ul { + padding: 0; + list-style: none; + margin: 10px 10px 0 10px; + + li a { + display: block; + padding: 10px; + } + } +} diff --git a/content/developers/pages/index.tsx b/content/developers/pages/index.tsx new file mode 100644 index 000000000..920628a72 --- /dev/null +++ b/content/developers/pages/index.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import Layout from '@theme/Layout'; +import Link from '@docusaurus/Link'; +import Hero from '../../../src/components/common/Hero'; +import blogPosts from '../../../.docusaurus/docusaurus-plugin-content-blog/developers-blog/blog-post-list-prop-developers-blog.json'; + +import './index.scss'; +import NavigationLinks from '../../../src/components/navigationLinks/NavigationLinks'; + +export default function Index() { + return ( + + +

Get started developing with or for Jellyfin today.

+
+ Not a developer? Go to the user documentation +
+
+
+
+
+ + + { + return { url: item.permalink, name: item.title }; + })} + /> + + +
+
+
+
+ ); +} diff --git a/docusaurus.config.ts b/docusaurus.config.ts index a7b344f2a..678b37f53 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -36,21 +36,23 @@ const config: Config = { src: 'images/logo.svg' }, items: [ - { to: 'posts', label: 'Blog', position: 'right' }, { to: 'downloads', - label: 'Downloads', - position: 'right' + label: 'Downloads' }, { - to: 'contribute', - label: 'Contribute', - position: 'right' + to: 'posts', + label: 'Blog' }, { - type: 'doc', - docId: 'index', - label: 'Documentation', + type: 'docSidebar', + sidebarId: 'docs', + label: 'Documentation' + }, + + { + to: 'developers', + label: 'Developers', position: 'right' }, { @@ -123,6 +125,38 @@ Site content is licensed {text}; +} + +export default Banner; diff --git a/src/components/navigationLinks/NavigationLinks.tsx b/src/components/navigationLinks/NavigationLinks.tsx new file mode 100644 index 000000000..7e0d8931b --- /dev/null +++ b/src/components/navigationLinks/NavigationLinks.tsx @@ -0,0 +1,23 @@ +import React from 'react'; + +import Section from '../section/Section'; +import Link from '@docusaurus/Link'; + +interface Page { + url: string; + name: string; +} + +export default function NavigationLinks({ title, pages }: { title: string; pages: Page[] }) { + return ( +
+
    + {pages.map((page, index) => ( +
  • + {page.name} +
  • + ))} +
+
+ ); +} diff --git a/src/components/section/Section.tsx b/src/components/section/Section.tsx new file mode 100644 index 000000000..1aa7e2e8c --- /dev/null +++ b/src/components/section/Section.tsx @@ -0,0 +1,12 @@ +import React, { ReactNode } from 'react'; + +export default function Section({ title, children }: { title: string; children: ReactNode }) { + return ( +
+
+

{title}

+ {children} +
+
+ ); +} diff --git a/src/pages/contribute.tsx b/src/pages/contribute.tsx index dd328e55d..931d3381f 100644 --- a/src/pages/contribute.tsx +++ b/src/pages/contribute.tsx @@ -29,7 +29,7 @@ export default function Contribute() {

Before contributing, please read over our{' '} Community Standards and  - Contributing Guide. + Contributing Guide.

diff --git a/src/theme/Root.tsx b/src/theme/Root.tsx new file mode 100644 index 000000000..e49966d2d --- /dev/null +++ b/src/theme/Root.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { useLocation } from '@docusaurus/router'; +import Banner from '../components/banner/Banner'; + +/** + * The root view wrapper for the entire app. + */ +export default function Root({ children }: { children: React.ReactNode }) { + const isInDevelopersSection = useLocation().pathname.startsWith('/developers'); + + return ( + <> + {isInDevelopersSection && ( + + )} + {children} + + ); +}