Compare commits
7 Commits
preview-pl
...
preview-se
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8cfd07b46 | ||
|
|
5eaa3ebb67 | ||
|
|
39ae32f509 | ||
|
|
c2977f6430 | ||
|
|
92504b7864 | ||
|
|
018e04a657 | ||
|
|
e503de323a |
4
.github/cliff.toml
vendored
4
.github/cliff.toml
vendored
@@ -33,9 +33,9 @@ body = """
|
||||
{{ self::print_commit(commit=commit) }}
|
||||
{%- endfor %}
|
||||
{%- for commit in commits %}
|
||||
{%- if not commit.scope -%}
|
||||
{%- if not commit.scope %}
|
||||
{{ self::print_commit(commit=commit) }}
|
||||
{%- endif -%}
|
||||
{%- endif %}
|
||||
{%- endfor -%}
|
||||
{%- endfor -%}
|
||||
|
||||
|
||||
2
.github/workflows/docs-deploy.yml
vendored
2
.github/workflows/docs-deploy.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- legacy-jellyseerr
|
||||
- develop
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- 'gen-docs/**'
|
||||
|
||||
20
.github/workflows/release.yml
vendored
20
.github/workflows/release.yml
vendored
@@ -279,17 +279,17 @@ jobs:
|
||||
--certificate-identity "https://github.com/${{ github.workflow_ref }}" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
|
||||
|
||||
- name: Verify attestations
|
||||
run: |
|
||||
cosign verify-attestation "ghcr.io/${{ github.repository }}@${{ needs.publish.outputs.image_digest }}" \
|
||||
--type cyclonedx \
|
||||
--certificate-identity "https://github.com/${{ github.workflow_ref }}" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" > /dev/null
|
||||
# - name: Verify attestations
|
||||
# run: |
|
||||
# cosign verify-attestation "ghcr.io/${{ github.repository }}@${{ needs.publish.outputs.image_digest }}" \
|
||||
# --type cyclonedx \
|
||||
# --certificate-identity "https://github.com/${{ github.workflow_ref }}" \
|
||||
# --certificate-oidc-issuer "https://token.actions.githubusercontent.com" > /dev/null
|
||||
|
||||
cosign verify-attestation "${{ env.DOCKER_HUB }}@${{ needs.publish.outputs.image_digest }}" \
|
||||
--type cyclonedx \
|
||||
--certificate-identity "https://github.com/${{ github.workflow_ref }}" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" > /dev/null
|
||||
# cosign verify-attestation "${{ env.DOCKER_HUB }}@${{ needs.publish.outputs.image_digest }}" \
|
||||
# --type cyclonedx \
|
||||
# --certificate-identity "https://github.com/${{ github.workflow_ref }}" \
|
||||
# --certificate-oidc-issuer "https://token.actions.githubusercontent.com" > /dev/null
|
||||
|
||||
publish-release:
|
||||
name: Publish release
|
||||
|
||||
34
README.md
34
README.md
@@ -1,6 +1,6 @@
|
||||
<div align="center">⚠️ <strong>NOTE:</strong> We are currently in the process of merging Overseerr and Jellyseerr into this unified repository.</div>
|
||||
|
||||
<h1 align="center" style="font-size: 4em;">🚧 Seerr</h1>
|
||||
<p align="center">
|
||||
<img src="./public/logo_full.svg" alt="Seerr" style="margin: 20px 0;">
|
||||
</p>
|
||||
<p align="center">
|
||||
<img src="https://github.com/seerr-team/seerr/actions/workflows/release.yml/badge.svg" alt="Seerr Release" />
|
||||
<img src="https://github.com/seerr-team/seerr/actions/workflows/ci.yml/badge.svg" alt="Seerr CI">
|
||||
@@ -32,31 +32,19 @@ With more features on the way! Check out our [issue tracker](/../../issues) to s
|
||||
|
||||
## Getting Started
|
||||
|
||||
For instructions on how to install and run **Jellyseerr**, please refer to the official documentation:
|
||||
Check out our documentation for instructions on how to install and run Seerr:
|
||||
|
||||
https://docs.seerr.dev/getting-started/
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Seerr is not officially released yet.**
|
||||
> The project is currently available **only on the `develop` branch** and is intended for **beta testing only**.
|
||||
|
||||
The documentation linked above is for running the **latest Jellyseerr** release.
|
||||
|
||||
> [!WARNING]
|
||||
> If you are migrating from **Overseerr** to **Seerr** for beta testing, **do not follow the Jellyseerr latest setup guide**.
|
||||
|
||||
Instead, follow the dedicated migration guide (with `:develop` tag):
|
||||
https://github.com/seerr-team/seerr/blob/develop/docs/migration-guide.mdx
|
||||
|
||||
> [!CAUTION]
|
||||
> **DO NOT run Jellyseerr (latest) using an existing Overseerr database. This includes third-party images with `seerr:latest` (as it points to jellyseerr 2.7.3 and not seerr.**
|
||||
> Doing so **may cause database corruption and/or irreversible data loss and/or weird unintended behaviour**.
|
||||
|
||||
For migration assistance, beta testing questions, or troubleshooting, please join our **Discord** and ask for support there.
|
||||
|
||||
## Preview
|
||||
|
||||
<img src="./public/preview.jpg">
|
||||
<img src="./public/preview.jpg" alt="Seerr application preview" />
|
||||
|
||||
## Migrating from Overseerr/Jellyseerr to Seerr
|
||||
|
||||
Read our [release announcement](https://docs.seerr.dev/blog/seerr-release) to learn what Seerr means for Jellyseerr and Overseerr users.
|
||||
|
||||
Please follow our [migration guide](https://docs.seerr.dev/migration-guide) for detailed instructions on migrating from Overseerr or Jellyseerr.
|
||||
|
||||
## Support
|
||||
|
||||
|
||||
@@ -200,14 +200,15 @@ Summary of changes :
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Nix (Third-party installation methods)
|
||||
## Third-party installation methods
|
||||
### Nix
|
||||
|
||||
Waiting for https://github.com/NixOS/nixpkgs/pull/450096 and https://github.com/NixOS/nixpkgs/pull/450093
|
||||
|
||||
### AUR (Third-party installation methods)
|
||||
### AUR
|
||||
|
||||
See https://aur.archlinux.org/packages/seerr
|
||||
|
||||
### TrueNAS (Third-party installation methods)
|
||||
### TrueNAS
|
||||
|
||||
Waiting for https://github.com/truenas/apps/issues/3374
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// previously cached resources to be updated from the network.
|
||||
// This variable is intentionally declared and unused.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const OFFLINE_VERSION = 4;
|
||||
const OFFLINE_VERSION = 5;
|
||||
const CACHE_NAME = 'offline';
|
||||
// Customize this with a different URL if needed.
|
||||
const OFFLINE_URL = '/offline.html';
|
||||
|
||||
@@ -27,13 +27,10 @@ export interface PlexLibraryItem {
|
||||
Media: Media[];
|
||||
}
|
||||
|
||||
interface PlexLibraryResponseRaw {
|
||||
interface PlexLibraryResponse {
|
||||
MediaContainer: {
|
||||
totalSize?: number;
|
||||
size?: number;
|
||||
Metadata?: PlexLibraryItem[];
|
||||
Directory?: PlexLibraryItem[];
|
||||
[key: string]: unknown;
|
||||
totalSize: number;
|
||||
Metadata: PlexLibraryItem[];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -180,7 +177,7 @@ class PlexAPI extends ExternalAPI {
|
||||
id: string,
|
||||
{ offset = 0, size = 50 }: { offset?: number; size?: number } = {}
|
||||
): Promise<{ totalSize: number; items: PlexLibraryItem[] }> {
|
||||
const response = await this.get<PlexLibraryResponseRaw>(
|
||||
const response = await this.get<PlexLibraryResponse>(
|
||||
`/library/sections/${id}/all?includeGuids=1`,
|
||||
{
|
||||
headers: {
|
||||
@@ -190,26 +187,9 @@ class PlexAPI extends ExternalAPI {
|
||||
}
|
||||
);
|
||||
|
||||
const container = response.MediaContainer;
|
||||
const metadataLength = container.Metadata?.length ?? 0;
|
||||
const directoryLength = container.Directory?.length ?? 0;
|
||||
|
||||
logger.debug('Plex getLibraryContents raw response', {
|
||||
label: 'Plex API',
|
||||
libraryId: id,
|
||||
offset,
|
||||
metadataLength,
|
||||
directoryLength,
|
||||
totalSize: container.totalSize,
|
||||
size: container.size,
|
||||
keys: Object.keys(container).filter((k) =>
|
||||
['Metadata', 'Directory', 'totalSize', 'size'].includes(k)
|
||||
),
|
||||
});
|
||||
|
||||
return {
|
||||
totalSize: container.totalSize ?? 0,
|
||||
items: container.Metadata ?? [],
|
||||
totalSize: response.MediaContainer.totalSize,
|
||||
items: response.MediaContainer.Metadata ?? [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -241,7 +221,7 @@ class PlexAPI extends ExternalAPI {
|
||||
},
|
||||
mediaType: 'movie' | 'show'
|
||||
): Promise<PlexLibraryItem[]> {
|
||||
const response = await this.get<PlexLibraryResponseRaw>(
|
||||
const response = await this.get<PlexLibraryResponse>(
|
||||
`/library/sections/${id}/all?type=${
|
||||
mediaType === 'show' ? '4' : '1'
|
||||
}&sort=addedAt%3Adesc&addedAt>>=${Math.floor(options.addedAt / 1000)}`,
|
||||
@@ -253,21 +233,7 @@ class PlexAPI extends ExternalAPI {
|
||||
}
|
||||
);
|
||||
|
||||
const container = response.MediaContainer;
|
||||
const items = container.Metadata ?? [];
|
||||
|
||||
logger.debug('Plex getRecentlyAdded raw response', {
|
||||
label: 'Plex API',
|
||||
libraryId: id,
|
||||
mediaType,
|
||||
addedAtFilter: options.addedAt,
|
||||
addedAtFilterDate: new Date(options.addedAt).toISOString(),
|
||||
metadataLength: container.Metadata?.length ?? 0,
|
||||
directoryLength: container.Directory?.length ?? 0,
|
||||
itemsReturned: items.length,
|
||||
});
|
||||
|
||||
return items;
|
||||
return response.MediaContainer.Metadata;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ class ServarrBase<QueueItemAppendT> extends ExternalAPI {
|
||||
apiKey,
|
||||
cacheName,
|
||||
apiName,
|
||||
timeout = 5000,
|
||||
timeout = 60000,
|
||||
}: {
|
||||
url: string;
|
||||
apiKey: string;
|
||||
|
||||
@@ -90,41 +90,22 @@ class PlexScanner
|
||||
if (this.isRecentOnly) {
|
||||
for (const library of this.libraries) {
|
||||
this.currentLibrary = library;
|
||||
const addedAtOpt = library.lastScan
|
||||
? {
|
||||
// We remove 10 minutes from the last scan as a buffer
|
||||
addedAt: library.lastScan - 1000 * 60 * 10,
|
||||
}
|
||||
: undefined;
|
||||
this.log(
|
||||
`Beginning to process recently added for library: ${library.name}`,
|
||||
'info',
|
||||
{
|
||||
lastScan: library.lastScan,
|
||||
addedAtFilter: addedAtOpt?.addedAt,
|
||||
addedAtFilterDate: addedAtOpt
|
||||
? new Date(addedAtOpt.addedAt).toISOString()
|
||||
: undefined,
|
||||
}
|
||||
{ lastScan: library.lastScan }
|
||||
);
|
||||
const libraryItems = await this.plexClient.getRecentlyAdded(
|
||||
library.id,
|
||||
addedAtOpt ?? {
|
||||
addedAt: Date.now() - 1000 * 60 * 60,
|
||||
},
|
||||
library.lastScan
|
||||
? {
|
||||
// We remove 10 minutes from the last scan as a buffer
|
||||
addedAt: library.lastScan - 1000 * 60 * 10,
|
||||
}
|
||||
: undefined,
|
||||
library.type
|
||||
);
|
||||
|
||||
this.log(
|
||||
`Recently added fetched ${libraryItems.length} items for library: ${library.name}`,
|
||||
'debug',
|
||||
{
|
||||
libraryId: library.id,
|
||||
itemCount: libraryItems.length,
|
||||
lastScanWillUpdateTo: Date.now(),
|
||||
}
|
||||
);
|
||||
|
||||
// Bundle items up by rating keys
|
||||
this.items = uniqWith(libraryItems, (mediaA, mediaB) => {
|
||||
if (mediaA.grandparentRatingKey && mediaB.grandparentRatingKey) {
|
||||
|
||||
Reference in New Issue
Block a user