Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Komga implementation #943

Merged
merged 70 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
4f15900
komga implementation
Jacob-Tate Aug 2, 2024
b5b39cd
Updated Komga integration to use SSE
Jacob-Tate Aug 4, 2024
238b146
Merge branch 'IgnisDa:main' into komga_impl
Jacob-Tate Aug 4, 2024
0e1c957
Finalizing implementation
Jacob-Tate Aug 5, 2024
83105fe
Added Komga Documentation
Jacob-Tate Aug 5, 2024
9fc4687
Placed Cargo.toml in Alphabetic Order
Jacob-Tate Aug 8, 2024
6bd89bd
Merge branch 'IgnisDa:main' into komga_impl
Jacob-Tate Aug 8, 2024
3e1f70a
Changed Komga into a Yank Integration.
Jacob-Tate Aug 8, 2024
86e2c5c
Removed provider specifics as it wasnt really needed.
Jacob-Tate Aug 8, 2024
b43df5b
Reverted order
Jacob-Tate Aug 8, 2024
834f3ff
Updated komga documentation
Jacob-Tate Aug 8, 2024
fb43a33
Fixed a bug where some manga have imported with the incorrect number
Jacob-Tate Aug 8, 2024
0b38409
docs: address formatting issues
IgnisDa Aug 9, 2024
6ab65c4
build(backend): pin `eventsource-stream` version
IgnisDa Aug 9, 2024
5aab4c8
chore(backend): format files
IgnisDa Aug 9, 2024
828ef34
refactor(backend): move integration to dedicated folder
IgnisDa Aug 9, 2024
a7a2b55
Fixed a bug where some tachiyomi clients report in random order.
Jacob-Tate Aug 10, 2024
8920c8a
Removed provider specifics from UpdateUserInntegrationInput
Jacob-Tate Aug 10, 2024
647cf47
Merge remote-tracking branch 'refs/remotes/origin/main' into komga_impl
Jacob-Tate Aug 10, 2024
ff38c8c
Fixed order of includes to match original.
Jacob-Tate Aug 10, 2024
ebd9be3
Fixed order of imports for komga.rs
Jacob-Tate Aug 10, 2024
54cd048
chore(backend): run formatter
IgnisDa Aug 11, 2024
94ba3f6
chore(backend): some housekeeping
IgnisDa Aug 11, 2024
57bff2d
fix(backend): type checks
IgnisDa Aug 11, 2024
ba0980d
chore(backend): add newline
IgnisDa Aug 11, 2024
72157b4
feat(backend): yank integrations data right on application startup
IgnisDa Aug 11, 2024
ecaf309
chore(backend): add a debug statement
IgnisDa Aug 11, 2024
30c9b78
Removed Option from find_provider_and_id
Jacob-Tate Aug 11, 2024
bc28164
Merge branch 'main' into komga_impl
IgnisDa Aug 11, 2024
302cb13
Run CI Empty Commit
Jacob-Tate Aug 11, 2024
afd70a8
chore: empty commit
IgnisDa Aug 12, 2024
c4070e3
ci: additional check for maintainer
IgnisDa Aug 14, 2024
5112364
merge origin
Jacob-Tate Aug 17, 2024
3d21694
Komga Implementation
Jacob-Tate Aug 17, 2024
703dc30
Merge branch 'refs/heads/komga' into komga_impl
Jacob-Tate Aug 17, 2024
1b02fc5
Resolve merge errors
Jacob-Tate Aug 17, 2024
4469588
Merge branch 'komga_impl' of https://github.com/jacob-tate/ryot into …
IgnisDa Aug 17, 2024
5c9c4f0
ci: revert ci changes
IgnisDa Aug 17, 2024
01f8d7f
chore(integrations): import structure
IgnisDa Aug 17, 2024
f2397b1
build(integrations): remove uneeded package
IgnisDa Aug 17, 2024
cc8178d
ci: change computation of image names
IgnisDa Aug 17, 2024
36bd0e3
ci: hardcode docker username
IgnisDa Aug 17, 2024
3180b9b
ci: enable hardmode
IgnisDa Aug 17, 2024
9b37a40
ci: quote names
IgnisDa Aug 17, 2024
6f6c2c9
ci: no heredoc fuckery
IgnisDa Aug 17, 2024
e3e0905
ci: change name of image
IgnisDa Aug 17, 2024
dd54876
ci: remove docker for forks
IgnisDa Aug 17, 2024
12cae01
ci: update condition
IgnisDa Aug 17, 2024
eccf2c4
ci: logging
IgnisDa Aug 17, 2024
6debb41
ci: use gh actions script
IgnisDa Aug 17, 2024
a9d23bb
ci: print all outputs
IgnisDa Aug 17, 2024
ea960dc
ci: use correct image names
IgnisDa Aug 17, 2024
c41b365
ci: use correct owner name
IgnisDa Aug 17, 2024
ba08f71
Upgraded to a trait model
Jacob-Tate Aug 17, 2024
fe023cd
Migrated jellyfin to trait
Jacob-Tate Aug 17, 2024
2834ac2
Fixed visibility of function
Jacob-Tate Aug 17, 2024
0f528a2
Emby Integration Migration
Jacob-Tate Aug 17, 2024
110d25c
Plex Integration Migration
Jacob-Tate Aug 17, 2024
bc5dfcc
Audiobookshelf Integration Migration (partial)
Jacob-Tate Aug 17, 2024
45db825
Kodi Integration Migration
Jacob-Tate Aug 18, 2024
5f131ed
Sonarr Integration Migration
Jacob-Tate Aug 18, 2024
be9c608
Radarr Integration Migration
Jacob-Tate Aug 18, 2024
3fc61c0
Ran formatter
Jacob-Tate Aug 18, 2024
8ecc8a6
chore: run rust formatter
IgnisDa Aug 18, 2024
79c6c05
refactor(integrations): some import changes
IgnisDa Aug 18, 2024
6715016
Finished implementing Audiobookshelf
Jacob-Tate Aug 18, 2024
3eebdb4
Undid accidental change
Jacob-Tate Aug 18, 2024
2394396
Merge branch 'IgnisDa:main' into komga_impl
Jacob-Tate Aug 19, 2024
ec018ed
chore(backend): apply formatter
IgnisDa Aug 19, 2024
c1eba11
Merge branch 'main' into komga_impl
IgnisDa Aug 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions apps/backend/src/background.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ pub async fn sync_integrations_data(
_information: ScheduledJob,
misc_service: Data<Arc<MiscellaneousService>>,
) -> Result<(), Error> {
tracing::trace!("Getting data from sse integrations for all users");
misc_service.sse_integrations_data().await.unwrap();
tracing::trace!("Getting data from yanked integrations for all users");
misc_service.yank_integrations_data().await.unwrap();
tracing::trace!("Sending data for push integrations for all users");
Expand Down Expand Up @@ -87,10 +85,6 @@ pub async fn perform_core_application_job(
misc_service
.yank_integrations_data_for_user(&user_id)
.await
.ok();
misc_service
.sse_integrations_data_for_user(&user_id)
.await
.is_ok()
}
CoreApplicationJob::BulkProgressUpdate(user_id, input) => misc_service
Expand Down
1 change: 1 addition & 0 deletions apps/backend/src/entities/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub struct Model {
#[graphql(skip_input)]
pub last_triggered_on: Option<DateTimeUtc>,
#[sea_orm(column_type = "Json")]
#[graphql(skip)]
pub provider_specifics: Option<IntegrationProviderSpecifics>,
}

Expand Down
112 changes: 12 additions & 100 deletions apps/backend/src/miscellaneous.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5499,7 +5499,7 @@ impl MiscellaneousService {
}
let lot = match input.provider {
IntegrationProvider::Audiobookshelf => IntegrationLot::Yank,
IntegrationProvider::Komga => IntegrationLot::SSE,
IntegrationProvider::Komga => IntegrationLot::Yank,
IntegrationProvider::Radarr | IntegrationProvider::Sonarr => IntegrationLot::Push,
_ => IntegrationLot::Sink,
};
Expand Down Expand Up @@ -5543,13 +5543,6 @@ impl MiscellaneousService {
if let Some(d) = input.is_disabled {
db_integration.is_disabled = ActiveValue::Set(Some(d));
}
if let Some(d) = input.provider_specifics {
let mut existing_specifics =
db_integration.provider_specifics.clone().unwrap().clone().unwrap().clone();

existing_specifics.komga_provider = d.komga_provider;
db_integration.provider_specifics = ActiveValue::Set(Some(existing_specifics));
}
db_integration.update(&self.db).await?;
Ok(true)
}
Expand Down Expand Up @@ -5754,83 +5747,6 @@ impl MiscellaneousService {
.collect()
}

pub async fn sse_integrations_data_for_user(&self, user_id: &String) -> Result<bool> {
let preferences = self.user_preferences(user_id).await?;
if preferences.general.disable_integrations {
return Ok(false);
}
let integrations = Integration::find()
.filter(integration::Column::UserId.eq(user_id))
.all(&self.db)
.await?;
let mut progress_updates = vec![];
let mut collection_updates = vec![];
let mut to_update_integrations = vec![];
let integration_service = self.get_integration_service();
for integration in integrations.into_iter() {
if integration.is_disabled.unwrap_or_default() {
tracing::debug!("Integration {} is disabled", integration.id);
continue;
}
let response = match integration.provider {
IntegrationProvider::Komga => {
let specifics = integration.clone().provider_specifics.unwrap();

integration_service
.komga_progress(
&specifics.komga_base_url.unwrap(),
&specifics.komga_cookie.unwrap(),
specifics.komga_provider.unwrap(),
)
.await
},
_ => continue,
};
if let Ok((seen_progress, collection_progress)) = response {
collection_updates.extend(collection_progress);
to_update_integrations.push(integration.id.clone());
progress_updates.push((integration, seen_progress));
}
}
for (integration, progress_updates) in progress_updates.into_iter() {
for pu in progress_updates.into_iter() {
self.integration_progress_update(&integration, pu, user_id)
.await
.trace_ok();
}
}
for col_update in collection_updates.into_iter() {
let metadata::Model { id, .. } = self
.commit_metadata(CommitMediaInput {
lot: col_update.lot,
source: col_update.source,
identifier: col_update.identifier.clone(),
force_update: None,
})
.await?;
self.add_entity_to_collection(
user_id,
ChangeCollectionToEntityInput {
creator_user_id: user_id.to_owned(),
collection_name: col_update.collection,
metadata_id: Some(id.clone()),
..Default::default()
},
)
.await
.trace_ok();
}
Integration::update_many()
.filter(integration::Column::Id.is_in(to_update_integrations))
.col_expr(
integration::Column::LastTriggeredOn,
Expr::value(Utc::now()),
)
.exec(&self.db)
.await?;
Ok(true)
}

pub async fn yank_integrations_data_for_user(&self, user_id: &String) -> Result<bool> {
let preferences = self.user_preferences(user_id).await?;
if preferences.general.disable_integrations {
Expand Down Expand Up @@ -5861,6 +5777,17 @@ impl MiscellaneousService {
)
.await
},
IntegrationProvider::Komga => {
let specifics = integration.clone().provider_specifics.unwrap();

integration_service
.komga_progress(
&specifics.komga_base_url.unwrap(),
&specifics.komga_cookie.unwrap(),
specifics.komga_provider.unwrap(),
)
.await
},
_ => continue,
};
if let Ok((seen_progress, collection_progress)) = response {
Expand Down Expand Up @@ -5923,21 +5850,6 @@ impl MiscellaneousService {
Ok(())
}

pub async fn sse_integrations_data(&self) -> Result<()> {
let users_with_integrations = Integration::find()
.filter(integration::Column::Lot.eq(IntegrationLot::SSE))
.select_only()
.column(integration::Column::UserId)
.into_tuple::<String>()
.all(&self.db)
.await?;
for user_id in users_with_integrations {
tracing::debug!("sse integrations data for user {}", user_id);
self.sse_integrations_data_for_user(&user_id).await?;
}
Ok(())
}

pub async fn send_data_for_push_integrations(&self) -> Result<()> {
let users_with_integrations = Integration::find()
.filter(integration::Column::Lot.eq(IntegrationLot::Push))
Expand Down
31 changes: 3 additions & 28 deletions apps/frontend/app/routes/_dashboard.settings.integrations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,15 @@ import { dayjsLib } from "~/lib/generals";
import { useConfirmSubmit, useUserCollections } from "~/lib/hooks";
import { createToastHeaders, serverGqlService } from "~/lib/utilities.server";

const SSE_INTEGRATION = [
IntegrationProvider.Komga,
]
const YANK_INTEGRATIONS = [
IntegrationProvider.Audiobookshelf,
IntegrationProvider.Komga,
];
const PUSH_INTEGRATIONS = [
IntegrationProvider.Radarr,
IntegrationProvider.Sonarr,
];
const NO_SHOW_URL = [...SSE_INTEGRATION, ...YANK_INTEGRATIONS, ...PUSH_INTEGRATIONS];
const NO_SHOW_URL = [...YANK_INTEGRATIONS, ...PUSH_INTEGRATIONS];

export const loader = unstable_defineLoader(async ({ request }) => {
const [{ userIntegrations }] = await Promise.all([
Expand Down Expand Up @@ -191,11 +189,6 @@ const updateSchema = z.object({
minimumProgress: z.string().optional(),
maximumProgress: z.string().optional(),
isDisabled: zx.CheckboxAsString.optional(),
providerSpecifics: z
.object({
komgaProvider: z.nativeEnum(MediaSource),
})
.optional(),
});

export default function Page() {
Expand Down Expand Up @@ -473,7 +466,7 @@ const CreateIntegrationModal = (props: {
label="Select a provider"
name="providerSpecifics.komgaProvider"
required
data={[MediaSource.Anilist,MediaSource.Mal].map((is) => ({
data={[MediaSource.Anilist, MediaSource.Mal].map((is) => ({
label: changeCase(is),
value: is,
}))}
Expand Down Expand Up @@ -587,24 +580,6 @@ const UpdateIntegrationModal = (props: {
/>
</Group>
) : null}
{match(props.updateIntegrationData.provider)
.with(IntegrationProvider.Komga, () => (
<>
<Select
label="Select a provider"
name="providerSpecifics.komgaProvider"
required
defaultValue = {
props.updateIntegrationData?.providerSpecifics?.komgaProvider || undefined
}
data={[MediaSource.Anilist,MediaSource.Mal].map((is) => ({
label: changeCase(is),
value: is,
}))}
/>
</>
))
.otherwise(() => undefined)}
<Checkbox
name="isDisabled"
label="Pause integration"
Expand Down
104 changes: 47 additions & 57 deletions docs/content/integrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ services about changes. They can be of following types:
interval.
- _Push_: Ryot sends data to an external service at a periodic interval.
- _Sink_: An external client publishes progress updates to the Ryot server.
- _SSE_: A server publishes event data for clients to use. Ryot subscribes to the event stream to capture updates.

## Yank integrations

Expand All @@ -33,6 +32,53 @@ have a valid provider ID (Audible, ITunes or ISBN).
2. Go to your Ryot user settings and add the correct details as described in the
[yank](#yank-integrations) section.

### Komga

!!! warning

This will only import media that are in progress. An import process will be added in
the future

The [Komga](https://komga.org/) integration can sync all media if they
have a valid metadata provider.

#### Komga side steps
If you use [Komf](https://github.com/Snd-R/komf) or some similar metadata provider these urls will be
populated automatically. If you don't use komf youll either need to manually add the manga to your collection
or you can perform the following steps.
1. Navigate to the manga
2. Open the edit tab
3. Navigate to the Links tab
4. Create a link named `AniList` or `MyAnimeList` providing the respective url (not case-sensitive)

To retrieve your Cookie youll need to perform the following steps:
1. Log out of Komga
2. Log back in while selecting `Remember me`
3. Press F12
4. Navigate to the Network tab
5. Refresh the page
6. Select any of the urls and look for the `Cookie` Header
7. Copy the entire cookie it should look something like this `remember-me=REDACTED; SESSION=REDACTED`

If you are using NGINX as your reverse proxy youll probably need to add the following to your configuration
to allow the SSE stream to work correctly.

```nginx
# Needs to be added to the location / section of the file
# If you dont the transfers will be chunked so youll get updates every 1000-2000 events
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
proxy_buffering off;
proxy_cache off;
```
#### Ryot side steps
1. Obtain your cookie as described above
2. Create the integration and select Komga as the source
3. Provide your BaseURL. Should look something like this `http://komga.acme.com` or `http://127.0.0.1:25600`
4. Provide your Cookie.
5. Provide your prefered metadata provider it will attempt the others if the first doesn't work and will fallback to title search otherwise

## Push integrations

Follow the same instructions as the [yank](#yank-integrations) integrations to add one.
Expand Down Expand Up @@ -138,59 +184,3 @@ TMDb ID attached to their metadata.
the zipped addon to your Kodi instance. Once installed, it will be visible under
the "Services" sub category named "Ryot".
4. Click on "Configure" to fill in the correct details.


## SSE integrations
For each integration you want to enable, credentials for the external server must be saved
to your profile. To do so, go to the "Settings" tab and add a new integration under the
"Integrations" tab.

You can configure the interval at which the data is handled from the sse listener using the
`integration.sync_every_minutes` configuration key. Defaults to `5` (minutes).

### Komga

!!! warning

This will only import media that are in progress. An import process will be added in
the future

The [Komga](https://komga.org/) integration can sync all media if they
have a valid metadata provider.

#### Komga side steps
If you use [Komf](https://github.com/Snd-R/komf) or some similar metadata provider these urls will be
populated automatically. If you don't use komf youll either need to manually add the manga to your collection
or you can perform the following steps.
1. Navigate to the manga
2. Open the edit tab
3. Navigate to the Links tab
4. Create a link named `AniList` or `MyAnimeList` providing the respective url (not case-sensitive)

To retrieve your Cookie youll need to perform the following steps:
1. Log out of Komga
2. Log back in while selecting `Remember me`
3. Press F12
4. Navigate to the Network tab
5. Refresh the page
6. Select any of the urls and look for the `Cookie` Header
7. Copy the entire cookie it should look something like this `remember-me=REDACTED; SESSION=REDACTED`

If you are using NGINX as your reverse proxy youll probably need to add the following to your configuration
to allow the SSE stream to work correctly.

```nginx
# Needs to be added to the location / section of the file
# If you dont the transfers will be chunked so youll get updates every 1000-2000 events
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
proxy_buffering off;
proxy_cache off;
```
#### Ryot side steps
1. Obtain your cookie as described above
2. Create the integration and select Komga as the source
3. Provide your BaseURL. Should look something like this `komga.acme.com` or `127.0.0.1:25600`
4. Provide your Cookie.
5. Provide your prefered metadata provider it will attempt the others if the first doesn't work and will fallback to title search otherwise
1 change: 0 additions & 1 deletion libs/database/src/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,6 @@ pub enum IntegrationLot {
Yank,
Sink,
Push,
SSE
}

#[derive(
Expand Down
Loading