├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml ├── runner-files │ └── ci-gradle.properties └── workflows │ ├── build_pull_request.yml │ ├── build_push.yml │ ├── docker_build_stable.yml │ ├── publish.yml │ └── winget.yml ├── .gitignore ├── AndroidCompat ├── Config │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── java │ │ └── xyz │ │ └── nulldev │ │ └── ts │ │ └── config │ │ ├── ApplicationRootDir.kt │ │ ├── ConfigManager.kt │ │ ├── ConfigManagerModule.kt │ │ ├── ConfigModule.kt │ │ ├── Logging.kt │ │ └── util │ │ └── ConfigExtensions.kt ├── build.gradle.kts ├── getAndroid.ps1 ├── getAndroid.sh └── src │ └── main │ ├── java │ ├── android │ │ ├── annotation │ │ │ ├── AnimRes.java │ │ │ ├── AnimatorRes.java │ │ │ ├── AnyRes.java │ │ │ ├── AppIdInt.java │ │ │ ├── ArrayRes.java │ │ │ ├── AttrRes.java │ │ │ ├── BinderThread.java │ │ │ ├── BoolRes.java │ │ │ ├── CallSuper.java │ │ │ ├── CheckResult.java │ │ │ ├── ColorInt.java │ │ │ ├── ColorRes.java │ │ │ ├── DimenRes.java │ │ │ ├── DrawableRes.java │ │ │ ├── FloatRange.java │ │ │ ├── FractionRes.java │ │ │ ├── IdRes.java │ │ │ ├── IntDef.java │ │ │ ├── IntRange.java │ │ │ ├── IntegerRes.java │ │ │ ├── InterpolatorRes.java │ │ │ ├── LayoutRes.java │ │ │ ├── MainThread.java │ │ │ ├── MenuRes.java │ │ │ ├── NonNull.java │ │ │ ├── Nullable.java │ │ │ ├── PluralsRes.java │ │ │ ├── RawRes.java │ │ │ ├── RequiresPermission.java │ │ │ ├── SdkConstant.java │ │ │ ├── Size.java │ │ │ ├── StringDef.java │ │ │ ├── StringRes.java │ │ │ ├── StyleRes.java │ │ │ ├── StyleableRes.java │ │ │ ├── SuppressLint.java │ │ │ ├── SystemApi.java │ │ │ ├── TargetApi.java │ │ │ ├── TestApi.java │ │ │ ├── TransitionRes.java │ │ │ ├── UiThread.java │ │ │ ├── UserIdInt.java │ │ │ ├── Widget.java │ │ │ ├── WorkerThread.java │ │ │ └── XmlRes.java │ │ ├── app │ │ │ ├── Application.java │ │ │ ├── PackageDeleteObserver.java │ │ │ ├── PackageInstallObserver.java │ │ │ └── Service.java │ │ ├── arch │ │ │ └── persistence │ │ │ │ └── db │ │ │ │ ├── SimpleSQLiteQuery.java │ │ │ │ ├── SupportSQLiteDatabase.java │ │ │ │ ├── SupportSQLiteOpenHelper.java │ │ │ │ ├── SupportSQLiteProgram.java │ │ │ │ ├── SupportSQLiteQuery.java │ │ │ │ ├── SupportSQLiteQueryBuilder.java │ │ │ │ ├── SupportSQLiteStatement.java │ │ │ │ └── framework │ │ │ │ ├── FrameworkSQLiteDatabase.java │ │ │ │ ├── FrameworkSQLiteOpenHelper.java │ │ │ │ ├── FrameworkSQLiteOpenHelperFactory.java │ │ │ │ ├── FrameworkSQLiteProgram.java │ │ │ │ └── FrameworkSQLiteStatement.java │ │ ├── content │ │ │ ├── ComponentCallbacks.java │ │ │ ├── ComponentCallbacks2.java │ │ │ ├── ComponentName.java │ │ │ ├── ContentValues.java │ │ │ ├── Context.java │ │ │ ├── ContextWrapper.java │ │ │ ├── Intent.java │ │ │ ├── SharedPreferences.java │ │ │ ├── pm │ │ │ │ ├── ApplicationInfo.java │ │ │ │ ├── FeatureInfo.java │ │ │ │ ├── InstantAppInfo.java │ │ │ │ ├── IntentFilterVerificationInfo.java │ │ │ │ ├── KeySet.java │ │ │ │ ├── PackageInfo.java │ │ │ │ ├── PackageItemInfo.java │ │ │ │ ├── PackageManager.java │ │ │ │ ├── PackageParser.java │ │ │ │ ├── Signature.java │ │ │ │ └── VersionedPackage.java │ │ │ └── res │ │ │ │ └── CompatibilityInfo.java │ │ ├── database │ │ │ ├── AbstractCursor.java │ │ │ ├── AbstractWindowedCursor.java │ │ │ ├── CharArrayBuffer.java │ │ │ ├── ContentObservable.java │ │ │ ├── CursorWindow.java │ │ │ ├── DataSetObservable.java │ │ │ ├── DatabaseUtils.java │ │ │ ├── DefaultDatabaseErrorHandler.java │ │ │ ├── Observable.java │ │ │ ├── SQLException.java │ │ │ └── sqlite │ │ │ │ ├── DatabaseObjectNotClosedException.java │ │ │ │ ├── SQLiteAbortException.java │ │ │ │ ├── SQLiteAccessPermException.java │ │ │ │ ├── SQLiteBindOrColumnIndexOutOfRangeException.java │ │ │ │ ├── SQLiteBlobTooBigException.java │ │ │ │ ├── SQLiteCantOpenDatabaseException.java │ │ │ │ ├── SQLiteClosable.java │ │ │ │ ├── SQLiteConstraintException.java │ │ │ │ ├── SQLiteCursor.java │ │ │ │ ├── SQLiteCursorDriver.java │ │ │ │ ├── SQLiteDatabase.java │ │ │ │ ├── SQLiteDatabaseConfiguration.java │ │ │ │ ├── SQLiteDatabaseCorruptException.java │ │ │ │ ├── SQLiteDatabaseLockedException.java │ │ │ │ ├── SQLiteDatatypeMismatchException.java │ │ │ │ ├── SQLiteDebug.java │ │ │ │ ├── SQLiteDirectCursorDriver.java │ │ │ │ ├── SQLiteDiskIOException.java │ │ │ │ ├── SQLiteDoneException.java │ │ │ │ ├── SQLiteException.java │ │ │ │ ├── SQLiteFullException.java │ │ │ │ ├── SQLiteGlobal.java │ │ │ │ ├── SQLiteMisuseException.java │ │ │ │ ├── SQLiteOpenHelper.java │ │ │ │ ├── SQLiteOutOfMemoryException.java │ │ │ │ ├── SQLiteProgram.java │ │ │ │ ├── SQLiteQuery.java │ │ │ │ ├── SQLiteQueryBuilder.java │ │ │ │ ├── SQLiteReadOnlyDatabaseException.java │ │ │ │ ├── SQLiteStatement.java │ │ │ │ ├── SQLiteStatementInfo.java │ │ │ │ ├── SQLiteTableLockedException.java │ │ │ │ ├── SQLiteTransactionListener.java │ │ │ │ ├── SqliteWrapper.java │ │ │ │ └── package.html │ │ ├── graphics │ │ │ ├── Bitmap.java │ │ │ ├── BitmapFactory.java │ │ │ ├── Canvas.java │ │ │ └── Rect.java │ │ ├── net │ │ │ ├── ConnectivityManager.java │ │ │ ├── NetworkInfo.java │ │ │ ├── ParseException.java │ │ │ ├── Uri.java │ │ │ └── WebAddress.java │ │ ├── os │ │ │ ├── BaseBundle.java │ │ │ ├── Build.java │ │ │ ├── Bundle.java │ │ │ ├── Environment.java │ │ │ ├── Parcelable.java │ │ │ ├── PersistableBundle.java │ │ │ ├── PowerManager.java │ │ │ ├── Process.java │ │ │ ├── SimpleClock.java │ │ │ ├── SystemClock.java │ │ │ ├── SystemProperties.java │ │ │ ├── UserHandle.java │ │ │ └── ZygoteProcess.java │ │ ├── preference │ │ │ └── PreferenceManager.kt │ │ ├── support │ │ │ ├── annotation │ │ │ │ ├── AnimRes.java │ │ │ │ ├── AnimatorRes.java │ │ │ │ ├── AnyRes.java │ │ │ │ ├── AnyThread.java │ │ │ │ ├── ArrayRes.java │ │ │ │ ├── AttrRes.java │ │ │ │ ├── BinderThread.java │ │ │ │ ├── BoolRes.java │ │ │ │ ├── CallSuper.java │ │ │ │ ├── CheckResult.java │ │ │ │ ├── ColorInt.java │ │ │ │ ├── ColorRes.java │ │ │ │ ├── DimenRes.java │ │ │ │ ├── Dimension.java │ │ │ │ ├── DrawableRes.java │ │ │ │ ├── FloatRange.java │ │ │ │ ├── FractionRes.java │ │ │ │ ├── IdRes.java │ │ │ │ ├── IntDef.java │ │ │ │ ├── IntRange.java │ │ │ │ ├── IntegerRes.java │ │ │ │ ├── InterpolatorRes.java │ │ │ │ ├── Keep.java │ │ │ │ ├── LayoutRes.java │ │ │ │ ├── MainThread.java │ │ │ │ ├── MenuRes.java │ │ │ │ ├── NonNull.java │ │ │ │ ├── Nullable.java │ │ │ │ ├── PluralsRes.java │ │ │ │ ├── Px.java │ │ │ │ ├── RawRes.java │ │ │ │ ├── RequiresApi.java │ │ │ │ ├── RequiresPermission.java │ │ │ │ ├── Size.java │ │ │ │ ├── StringDef.java │ │ │ │ ├── StringRes.java │ │ │ │ ├── StyleRes.java │ │ │ │ ├── StyleableRes.java │ │ │ │ ├── TransitionRes.java │ │ │ │ ├── UiThread.java │ │ │ │ ├── VisibleForTesting.java │ │ │ │ ├── WorkerThread.java │ │ │ │ ├── XmlRes.java │ │ │ │ └── package-info.java │ │ │ └── multidex │ │ │ │ └── MultiDex.java │ │ ├── text │ │ │ ├── Html.java │ │ │ ├── TextUtils.java │ │ │ └── format │ │ │ │ └── Formatter.java │ │ ├── util │ │ │ ├── ArrayMap.java │ │ │ ├── Base64.java │ │ │ ├── ContainerHelpers.java │ │ │ ├── Log.java │ │ │ └── MapCollections.java │ │ ├── view │ │ │ └── DisplayAdjustments.java │ │ ├── webkit │ │ │ ├── CookieManager.java │ │ │ ├── MimeTypeMap.java │ │ │ └── URLUtil.java │ │ └── widget │ │ │ ├── EditText.java │ │ │ └── Toast.java │ ├── androidx │ │ ├── core │ │ │ └── net │ │ │ │ └── Uri.kt │ │ └── preference │ │ │ ├── CheckBoxPreference.java │ │ │ ├── DialogPreference.java │ │ │ ├── EditTextPreference.java │ │ │ ├── ListPreference.java │ │ │ ├── MultiSelectListPreference.java │ │ │ ├── Preference.java │ │ │ ├── PreferenceScreen.java │ │ │ ├── SwitchPreferenceCompat.java │ │ │ └── TwoStatePreference.java │ ├── app │ │ └── cash │ │ │ └── quickjs │ │ │ ├── QuickJs.java │ │ │ └── QuickJsException.java │ ├── com │ │ ├── android │ │ │ └── internal │ │ │ │ └── util │ │ │ │ ├── ArrayUtils.java │ │ │ │ ├── FastXmlSerializer.java │ │ │ │ ├── Preconditions.java │ │ │ │ └── XmlUtils.java │ │ └── squareup │ │ │ └── duktape │ │ │ ├── Duktape.java │ │ │ ├── DuktapeException.java │ │ │ └── DuktapeStub.java │ ├── dalvik │ │ └── system │ │ │ ├── BaseDexClassLoader.java │ │ │ ├── CloseGuard.java │ │ │ └── PathClassLoader.java │ ├── io │ │ └── requery │ │ │ └── android │ │ │ └── database │ │ │ └── sqlite │ │ │ └── RequerySQLiteOpenHelperFactory.kt │ ├── libcore │ │ ├── net │ │ │ ├── MimeUtils.java │ │ │ └── UriCodec.java │ │ └── util │ │ │ └── EmptyArray.java │ ├── org │ │ └── json │ │ │ ├── JSON.java │ │ │ ├── JSONArray.java │ │ │ ├── JSONException.java │ │ │ ├── JSONObject.java │ │ │ ├── JSONStringer.java │ │ │ ├── JSONTokener.java │ │ │ └── package-info.java │ ├── rx.android.schedulers │ │ └── AndroidSchedulers.kt │ └── xyz │ │ └── nulldev │ │ └── androidcompat │ │ ├── AndroidCompat.kt │ │ ├── AndroidCompatInitializer.kt │ │ ├── AndroidCompatModule.kt │ │ ├── androidimpl │ │ ├── CustomContext.java │ │ └── FakePackageManager.java │ │ ├── config │ │ ├── ApplicationInfoConfigModule.kt │ │ ├── FilesConfigModule.kt │ │ └── SystemConfigModule.kt │ │ ├── db │ │ └── ScrollableResultSet.kt │ │ ├── info │ │ └── ApplicationInfoImpl.kt │ │ ├── io │ │ ├── AndroidFiles.kt │ │ └── sharedprefs │ │ │ ├── JavaSharedPreferences.kt │ │ │ └── JsonSharedPreferences.java │ │ ├── pm │ │ ├── InstalledPackage.kt │ │ ├── PackageController.kt │ │ └── PackageUtil.kt │ │ ├── replace │ │ └── java │ │ │ ├── text │ │ │ ├── NumberFormat.java │ │ │ └── SimpleDateFormat.java │ │ │ └── util │ │ │ ├── Calendar.java │ │ │ └── TimeZone.java │ │ ├── res │ │ ├── BuildConfigCompat.java │ │ ├── DrawableResource.kt │ │ ├── RCompat.java │ │ ├── Resource.kt │ │ └── StringResource.kt │ │ ├── service │ │ └── ServiceSupport.kt │ │ ├── util │ │ ├── KoinGlobalHelper.kt │ │ ├── SafePath.kt │ │ └── UriExtensions.kt │ │ └── webkit │ │ └── CookieManagerImpl.kt │ └── resources │ └── compat-reference.conf ├── CHANGELOG-TEMPLATE.md ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── build.gradle.kts ├── buildSrc ├── build.gradle.kts ├── settings.gradle.kts └── src │ └── main │ └── kotlin │ └── Constants.kt ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── renovate.json ├── scripts ├── bundler.sh └── resources │ ├── Suwayomi Launcher.bat │ ├── Suwayomi Launcher.command │ ├── deb │ ├── changelog │ ├── control │ ├── copyright │ ├── install │ ├── rules │ └── source │ │ ├── format │ │ └── include-binaries │ ├── msi │ └── suwayomi-server-x64.wxs │ ├── pkg │ ├── suwayomi-launcher.desktop │ ├── suwayomi-launcher.sh │ ├── suwayomi-server.desktop │ ├── suwayomi-server.sh │ └── systemd │ │ ├── suwayomi-server.conf │ │ ├── suwayomi-server.service │ │ ├── suwayomi-server.sysusers │ │ └── suwayomi-server.tmpfiles │ ├── suwayomi-launcher.sh │ └── suwayomi-server.sh ├── server ├── build.gradle.kts ├── i18n │ ├── build.gradle.kts │ └── src │ │ └── commonMain │ │ └── moko-resources │ │ ├── files │ │ └── languages.json │ │ └── values │ │ ├── base │ │ └── strings.xml │ │ └── es │ │ └── strings.xml └── src │ ├── main │ ├── kotlin │ │ ├── eu │ │ │ └── kanade │ │ │ │ └── tachiyomi │ │ │ │ ├── App.kt │ │ │ │ ├── AppInfo.kt │ │ │ │ ├── AppModule.kt │ │ │ │ ├── LICENSE │ │ │ │ ├── network │ │ │ │ ├── JavaScriptEngine.kt │ │ │ │ ├── MemoryCookieJar.kt │ │ │ │ ├── NetworkHelper.kt │ │ │ │ ├── OkHttpExtensions.kt │ │ │ │ ├── PersistentCookieJar.kt │ │ │ │ ├── PersistentCookieStore.kt │ │ │ │ ├── ProgressListener.kt │ │ │ │ ├── ProgressResponseBody.kt │ │ │ │ ├── Requests.kt │ │ │ │ └── interceptor │ │ │ │ │ ├── CloudflareInterceptor.kt │ │ │ │ │ ├── IgnoreGzipInterceptor.kt │ │ │ │ │ ├── RateLimitInterceptor.kt │ │ │ │ │ ├── SpecificHostRateLimitInterceptor.kt │ │ │ │ │ ├── UncaughtExceptionInterceptor.kt │ │ │ │ │ └── UserAgentInterceptor.kt │ │ │ │ ├── source │ │ │ │ ├── CatalogueSource.kt │ │ │ │ ├── ConfigurableSource.kt │ │ │ │ ├── Source.kt │ │ │ │ ├── SourceFactory.kt │ │ │ │ ├── UnmeteredSource.kt │ │ │ │ ├── local │ │ │ │ │ ├── LocalSource.kt │ │ │ │ │ ├── filter │ │ │ │ │ │ └── OrderBy.kt │ │ │ │ │ ├── image │ │ │ │ │ │ └── LocalCoverManager.kt │ │ │ │ │ ├── io │ │ │ │ │ │ ├── Archive.kt │ │ │ │ │ │ ├── Format.kt │ │ │ │ │ │ └── LocalSourceFileSystem.kt │ │ │ │ │ ├── loader │ │ │ │ │ │ ├── EpubPageLoader.kt │ │ │ │ │ │ ├── PageLoader.kt │ │ │ │ │ │ ├── RarPageLoader.kt │ │ │ │ │ │ ├── ReaderPage.kt │ │ │ │ │ │ └── ZipPageLoader.kt │ │ │ │ │ └── metadata │ │ │ │ │ │ ├── ComicInfo.kt │ │ │ │ │ │ ├── EpubFile.kt │ │ │ │ │ │ └── MangaDetails.kt │ │ │ │ ├── model │ │ │ │ │ ├── Filter.kt │ │ │ │ │ ├── FilterList.kt │ │ │ │ │ ├── MangasPage.kt │ │ │ │ │ ├── Page.kt │ │ │ │ │ ├── SChapter.kt │ │ │ │ │ ├── SChapterImpl.kt │ │ │ │ │ ├── SManga.kt │ │ │ │ │ ├── SMangaImpl.kt │ │ │ │ │ └── UpdateStrategy.kt │ │ │ │ └── online │ │ │ │ │ ├── HttpSource.kt │ │ │ │ │ ├── ParsedHttpSource.kt │ │ │ │ │ └── ResolvableSource.kt │ │ │ │ └── util │ │ │ │ ├── JsoupExtensions.kt │ │ │ │ ├── PkceUtil.kt │ │ │ │ ├── chapter │ │ │ │ ├── ChapterRecognition.kt │ │ │ │ └── ChapterSanitizer.kt │ │ │ │ ├── lang │ │ │ │ ├── CoroutinesExtensions.kt │ │ │ │ ├── Hash.kt │ │ │ │ └── StringExtensions.kt │ │ │ │ └── storage │ │ │ │ └── EpubFile.kt │ │ └── suwayomi │ │ │ └── tachidesk │ │ │ ├── Main.kt │ │ │ ├── global │ │ │ ├── GlobalAPI.kt │ │ │ ├── controller │ │ │ │ ├── GlobalMetaController.kt │ │ │ │ └── SettingsController.kt │ │ │ ├── impl │ │ │ │ ├── About.kt │ │ │ │ ├── AppUpdate.kt │ │ │ │ └── GlobalMeta.kt │ │ │ └── model │ │ │ │ └── table │ │ │ │ └── GlobalMetaTable.kt │ │ │ ├── graphql │ │ │ ├── AsDataFetcherResult.kt │ │ │ ├── GraphQL.kt │ │ │ ├── cache │ │ │ │ └── CustomCacheMap.kt │ │ │ ├── controller │ │ │ │ └── GraphQLController.kt │ │ │ ├── dataLoaders │ │ │ │ ├── CategoryDataLoader.kt │ │ │ │ ├── ChapterDataLoader.kt │ │ │ │ ├── ExtensionDataLoader.kt │ │ │ │ ├── MangaDataLoader.kt │ │ │ │ ├── MetaDataLoader.kt │ │ │ │ ├── SourceDataLoader.kt │ │ │ │ └── TrackDataLoader.kt │ │ │ ├── mutations │ │ │ │ ├── BackupMutation.kt │ │ │ │ ├── CategoryMutation.kt │ │ │ │ ├── ChapterMutation.kt │ │ │ │ ├── DownloadMutation.kt │ │ │ │ ├── ExtensionMutation.kt │ │ │ │ ├── ImageMutation.kt │ │ │ │ ├── InfoMutation.kt │ │ │ │ ├── MangaMutation.kt │ │ │ │ ├── MetaMutation.kt │ │ │ │ ├── SettingsMutation.kt │ │ │ │ ├── SourceMutation.kt │ │ │ │ ├── TrackMutation.kt │ │ │ │ └── UpdateMutation.kt │ │ │ ├── queries │ │ │ │ ├── BackupQuery.kt │ │ │ │ ├── CategoryQuery.kt │ │ │ │ ├── ChapterQuery.kt │ │ │ │ ├── DownloadQuery.kt │ │ │ │ ├── ExtensionQuery.kt │ │ │ │ ├── InfoQuery.kt │ │ │ │ ├── MangaQuery.kt │ │ │ │ ├── MetaQuery.kt │ │ │ │ ├── SettingsQuery.kt │ │ │ │ ├── SourceQuery.kt │ │ │ │ ├── TrackQuery.kt │ │ │ │ ├── UpdateQuery.kt │ │ │ │ └── filter │ │ │ │ │ └── Filter.kt │ │ │ ├── server │ │ │ │ ├── JavalinGraphQLRequestParser.kt │ │ │ │ ├── TachideskDataLoaderRegistryFactory.kt │ │ │ │ ├── TachideskGraphQLContextFactory.kt │ │ │ │ ├── TachideskGraphQLSchema.kt │ │ │ │ ├── TachideskGraphQLServer.kt │ │ │ │ ├── TemporaryFileStorage.kt │ │ │ │ ├── primitives │ │ │ │ │ ├── Cursor.kt │ │ │ │ │ ├── LongAsString.kt │ │ │ │ │ ├── NodeList.kt │ │ │ │ │ ├── OrderBy.kt │ │ │ │ │ ├── QueryResults.kt │ │ │ │ │ └── Upload.kt │ │ │ │ └── subscriptions │ │ │ │ │ ├── ApolloSubscriptionProtocolHandler.kt │ │ │ │ │ ├── ApolloSubscriptionSessionState.kt │ │ │ │ │ ├── FlowSubscriptionSource.kt │ │ │ │ │ ├── GraphQLSubscriptionHandler.kt │ │ │ │ │ └── SubscriptionOperationMessage.kt │ │ │ ├── subscriptions │ │ │ │ ├── DownloadSubscription.kt │ │ │ │ ├── InfoSubscription.kt │ │ │ │ └── UpdateSubscription.kt │ │ │ └── types │ │ │ │ ├── BackupTypes.kt │ │ │ │ ├── CategoryType.kt │ │ │ │ ├── ChapterType.kt │ │ │ │ ├── DownloadType.kt │ │ │ │ ├── ExtensionType.kt │ │ │ │ ├── MangaType.kt │ │ │ │ ├── MetaType.kt │ │ │ │ ├── SettingsType.kt │ │ │ │ ├── SourceType.kt │ │ │ │ ├── TrackType.kt │ │ │ │ ├── UpdateType.kt │ │ │ │ └── WebUIUpdateType.kt │ │ │ ├── i18n │ │ │ └── LocalizationHelper.kt │ │ │ ├── manga │ │ │ ├── MangaAPI.kt │ │ │ ├── controller │ │ │ │ ├── BackupController.kt │ │ │ │ ├── CategoryController.kt │ │ │ │ ├── DownloadController.kt │ │ │ │ ├── ExtensionController.kt │ │ │ │ ├── MangaController.kt │ │ │ │ ├── SourceController.kt │ │ │ │ ├── TrackController.kt │ │ │ │ └── UpdateController.kt │ │ │ ├── impl │ │ │ │ ├── Category.kt │ │ │ │ ├── CategoryManga.kt │ │ │ │ ├── Chapter.kt │ │ │ │ ├── ChapterDownloadHelper.kt │ │ │ │ ├── Library.kt │ │ │ │ ├── Manga.kt │ │ │ │ ├── MangaList.kt │ │ │ │ ├── Page.kt │ │ │ │ ├── Search.kt │ │ │ │ ├── Source.kt │ │ │ │ ├── ThumbnailDownloadHelper.kt │ │ │ │ ├── backup │ │ │ │ │ ├── BackupFlags.kt │ │ │ │ │ ├── models │ │ │ │ │ │ ├── Chapter.kt │ │ │ │ │ │ ├── ChapterImpl.kt │ │ │ │ │ │ ├── Manga.kt │ │ │ │ │ │ ├── MangaImpl.kt │ │ │ │ │ │ ├── Track.kt │ │ │ │ │ │ └── TrackImpl.kt │ │ │ │ │ └── proto │ │ │ │ │ │ ├── ProtoBackupBase.kt │ │ │ │ │ │ ├── ProtoBackupExport.kt │ │ │ │ │ │ ├── ProtoBackupImport.kt │ │ │ │ │ │ ├── ProtoBackupValidator.kt │ │ │ │ │ │ └── models │ │ │ │ │ │ ├── Backup.kt │ │ │ │ │ │ ├── BackupCategory.kt │ │ │ │ │ │ ├── BackupChapter.kt │ │ │ │ │ │ ├── BackupHistory.kt │ │ │ │ │ │ ├── BackupManga.kt │ │ │ │ │ │ ├── BackupSource.kt │ │ │ │ │ │ └── BackupTracking.kt │ │ │ │ ├── chapter │ │ │ │ │ └── ChapterForDownload.kt │ │ │ │ ├── download │ │ │ │ │ ├── DownloadManager.kt │ │ │ │ │ ├── Downloader.kt │ │ │ │ │ ├── fileProvider │ │ │ │ │ │ ├── ChaptersFilesProvider.kt │ │ │ │ │ │ ├── DownloadedFilesProvider.kt │ │ │ │ │ │ ├── FileDownloader.kt │ │ │ │ │ │ ├── FileRetriever.kt │ │ │ │ │ │ └── impl │ │ │ │ │ │ │ ├── ArchiveProvider.kt │ │ │ │ │ │ │ ├── FolderProvider.kt │ │ │ │ │ │ │ └── ThumbnailFileProvider.kt │ │ │ │ │ └── model │ │ │ │ │ │ ├── DownloadChapter.kt │ │ │ │ │ │ ├── DownloadState.kt │ │ │ │ │ │ ├── DownloadStatus.kt │ │ │ │ │ │ └── DownloadUpdate.kt │ │ │ │ ├── extension │ │ │ │ │ ├── Extension.kt │ │ │ │ │ ├── ExtensionsList.kt │ │ │ │ │ └── github │ │ │ │ │ │ ├── ExtensionGithubApi.kt │ │ │ │ │ │ └── OnlineExtension.kt │ │ │ │ ├── track │ │ │ │ │ ├── Track.kt │ │ │ │ │ └── tracker │ │ │ │ │ │ ├── DeletableTrackService.kt │ │ │ │ │ │ ├── Tracker.kt │ │ │ │ │ │ ├── TrackerManager.kt │ │ │ │ │ │ ├── TrackerPreferences.kt │ │ │ │ │ │ ├── anilist │ │ │ │ │ │ ├── Anilist.kt │ │ │ │ │ │ ├── AnilistApi.kt │ │ │ │ │ │ ├── AnilistInterceptor.kt │ │ │ │ │ │ └── AnilistModels.kt │ │ │ │ │ │ ├── bangumi │ │ │ │ │ │ ├── Bangumi.kt │ │ │ │ │ │ ├── BangumiApi.kt │ │ │ │ │ │ ├── BangumiInterceptor.kt │ │ │ │ │ │ └── BangumiModels.kt │ │ │ │ │ │ ├── kitsu │ │ │ │ │ │ ├── Kitsu.kt │ │ │ │ │ │ ├── KitsuApi.kt │ │ │ │ │ │ ├── KitsuDateHelper.kt │ │ │ │ │ │ ├── KitsuInterceptor.kt │ │ │ │ │ │ └── KitsuModels.kt │ │ │ │ │ │ ├── mangaupdates │ │ │ │ │ │ ├── MangaUpdates.kt │ │ │ │ │ │ ├── MangaUpdatesApi.kt │ │ │ │ │ │ ├── MangaUpdatesInterceptor.kt │ │ │ │ │ │ └── dto │ │ │ │ │ │ │ ├── Context.kt │ │ │ │ │ │ │ ├── Image.kt │ │ │ │ │ │ │ ├── ListItem.kt │ │ │ │ │ │ │ ├── Rating.kt │ │ │ │ │ │ │ ├── Record.kt │ │ │ │ │ │ │ ├── Series.kt │ │ │ │ │ │ │ ├── Status.kt │ │ │ │ │ │ │ └── Url.kt │ │ │ │ │ │ ├── model │ │ │ │ │ │ ├── Track.kt │ │ │ │ │ │ ├── TrackConvertor.kt │ │ │ │ │ │ ├── TrackImpl.kt │ │ │ │ │ │ └── TrackSearch.kt │ │ │ │ │ │ └── myanimelist │ │ │ │ │ │ ├── MyAnimeList.kt │ │ │ │ │ │ ├── MyAnimeListApi.kt │ │ │ │ │ │ ├── MyAnimeListInterceptor.kt │ │ │ │ │ │ └── MyAnimeListModels.kt │ │ │ │ ├── update │ │ │ │ │ ├── IUpdater.kt │ │ │ │ │ ├── UpdateJob.kt │ │ │ │ │ ├── UpdateStatus.kt │ │ │ │ │ ├── Updater.kt │ │ │ │ │ ├── UpdaterSocket.kt │ │ │ │ │ └── Websocket.kt │ │ │ │ └── util │ │ │ │ │ ├── BytecodeEditor.kt │ │ │ │ │ ├── DirName.kt │ │ │ │ │ ├── GetComicInfo.kt │ │ │ │ │ ├── PackageTools.kt │ │ │ │ │ ├── lang │ │ │ │ │ ├── ExposedExtensions.kt │ │ │ │ │ ├── FileExt.kt │ │ │ │ │ ├── List.kt │ │ │ │ │ └── RxCoroutineBridge.kt │ │ │ │ │ ├── network │ │ │ │ │ ├── OkHttp.kt │ │ │ │ │ └── UnzippingInterceptor.kt │ │ │ │ │ ├── source │ │ │ │ │ ├── GetCatalogueSource.kt │ │ │ │ │ └── StubSource.kt │ │ │ │ │ └── storage │ │ │ │ │ ├── FileDeletionHelper.kt │ │ │ │ │ ├── ImageResponse.kt │ │ │ │ │ ├── ImageUtil.kt │ │ │ │ │ ├── Io.kt │ │ │ │ │ └── OkioExtensions.kt │ │ │ └── model │ │ │ │ ├── dataclass │ │ │ │ ├── CategoryDataClass.kt │ │ │ │ ├── ChapterDataClass.kt │ │ │ │ ├── ExtensionDataClass.kt │ │ │ │ ├── MangaChapterDataClass.kt │ │ │ │ ├── MangaDataClass.kt │ │ │ │ ├── MangaTrackerDataClass.kt │ │ │ │ ├── PageDataClass.kt │ │ │ │ ├── PaginatedList.kt │ │ │ │ ├── SourceDataClass.kt │ │ │ │ ├── TrackRecordDataClass.kt │ │ │ │ ├── TrackSearchDataClass.kt │ │ │ │ └── TrackerDataClass.kt │ │ │ │ └── table │ │ │ │ ├── CategoryMangaTable.kt │ │ │ │ ├── CategoryMetaTable.kt │ │ │ │ ├── CategoryTable.kt │ │ │ │ ├── ChapterMetaTable.kt │ │ │ │ ├── ChapterTable.kt │ │ │ │ ├── ExtensionTable.kt │ │ │ │ ├── MangaMetaTable.kt │ │ │ │ ├── MangaTable.kt │ │ │ │ ├── PageTable.kt │ │ │ │ ├── SourceMetaTable.kt │ │ │ │ ├── SourceTable.kt │ │ │ │ ├── TrackRecordTable.kt │ │ │ │ └── TrackSearchTable.kt │ │ │ ├── opds │ │ │ ├── OpdsAPI.kt │ │ │ ├── constants │ │ │ │ └── OpdsConstants.kt │ │ │ ├── controller │ │ │ │ └── OpdsV1Controller.kt │ │ │ ├── dto │ │ │ │ ├── OpdsCategoryNavEntry.kt │ │ │ │ ├── OpdsChapterListAcqEntry.kt │ │ │ │ ├── OpdsChapterMetadataAcqEntry.kt │ │ │ │ ├── OpdsGenreNavEntry.kt │ │ │ │ ├── OpdsLanguageNavEntry.kt │ │ │ │ ├── OpdsLibraryUpdateAcqEntry.kt │ │ │ │ ├── OpdsMangaAcqEntry.kt │ │ │ │ ├── OpdsMangaDetails.kt │ │ │ │ ├── OpdsRootNavEntry.kt │ │ │ │ ├── OpdsSearchCriteria.kt │ │ │ │ ├── OpdsSourceNavEntry.kt │ │ │ │ └── OpdsStatusNavEntry.kt │ │ │ ├── impl │ │ │ │ └── OpdsFeedBuilder.kt │ │ │ ├── model │ │ │ │ ├── OpdsAuthorXml.kt │ │ │ │ ├── OpdsCategoryXml.kt │ │ │ │ ├── OpdsContentXml.kt │ │ │ │ ├── OpdsEntryXml.kt │ │ │ │ ├── OpdsFeedXml.kt │ │ │ │ ├── OpdsIndirectAcquisitionXml.kt │ │ │ │ ├── OpdsLinkXml.kt │ │ │ │ └── OpdsSummaryXml.kt │ │ │ ├── repository │ │ │ │ ├── ChapterRepository.kt │ │ │ │ ├── MangaRepository.kt │ │ │ │ └── NavigationRepository.kt │ │ │ └── util │ │ │ │ ├── OpdsDateUtil.kt │ │ │ │ ├── OpdsStringUtil.kt │ │ │ │ └── OpdsXmlUtil.kt │ │ │ ├── server │ │ │ ├── ConfigAdapters.kt │ │ │ ├── JavalinSetup.kt │ │ │ ├── Migration.kt │ │ │ ├── ServerConfig.kt │ │ │ ├── ServerSetup.kt │ │ │ ├── database │ │ │ │ ├── DBManager.kt │ │ │ │ ├── DBTransaction.util.kt │ │ │ │ └── migration │ │ │ │ │ ├── M0001_Initial.kt │ │ │ │ │ ├── M0002_ChapterTableIndexRename.kt │ │ │ │ │ ├── M0003_DefaultCategory.kt │ │ │ │ │ ├── M0004_AnimeTablesBatch1.kt │ │ │ │ │ ├── M0005_AnimeTablesBatch2.kt │ │ │ │ │ ├── M0006_AnimeTablesBatch3.kt │ │ │ │ │ ├── M0007_ChapterIsDownloaded.kt │ │ │ │ │ ├── M0008_ChapterPageCount.kt │ │ │ │ │ ├── M0009_ChapterLastReadAt.kt │ │ │ │ │ ├── M0010_MangaAndChapterMeta.kt │ │ │ │ │ ├── M0011_SourceDropPartOfFactorySource.kt │ │ │ │ │ ├── M0012_SourceIsNsfw.kt │ │ │ │ │ ├── M0013_MangaRealUrl.kt │ │ │ │ │ ├── M0014_MangaRemoveLengthLimit.kt │ │ │ │ │ ├── M0015_SourceAndExtensionLangAddLengthLimit.kt │ │ │ │ │ ├── M0016_ChapterIndexRenameToSourceOrder.kt │ │ │ │ │ ├── M0017_ChapterFetchedAt.kt │ │ │ │ │ ├── M0018_MangaInLibraryAt.kt │ │ │ │ │ ├── M0019_RemoveAnime.kt │ │ │ │ │ ├── M0020_AddMangaLastFetchedAtColumns.kt │ │ │ │ │ ├── M0021_GlobalAndCategoryMeta.kt │ │ │ │ │ ├── M0022_MangaThumbnailLastFetched.kt │ │ │ │ │ ├── M0023_CategoryMetaRefFix.kt │ │ │ │ │ ├── M0024_MangaUpdateStrategy.kt │ │ │ │ │ ├── M0025_ChapterRealUrl.kt │ │ │ │ │ ├── M0026_CategoryIncludeInUpdate.kt │ │ │ │ │ ├── M0027_AddDefaultCategory.kt │ │ │ │ │ ├── M0028_AddCascade.kt │ │ │ │ │ ├── M0029_DropMangaDefaultCategory.kt │ │ │ │ │ ├── M0030_FixDateUpload.kt │ │ │ │ │ ├── M0031_AddExtensionRepo.kt │ │ │ │ │ ├── M0032_FixExtensionRepos.kt │ │ │ │ │ ├── M0033_TrackRecord.kt │ │ │ │ │ ├── M0034_CategoryIncludeInDownload.kt │ │ │ │ │ ├── M0035_TrackSearch.kt │ │ │ │ │ ├── M0036_SourceMeta.kt │ │ │ │ │ ├── M0037_RemoveTrackRecordsOfUnsupportedTrackers.kt │ │ │ │ │ ├── M0038_RemoveTrackRecordsOfUnsupportedTrackersII.kt │ │ │ │ │ ├── M0039_DeleteDuplicatedChapters.kt │ │ │ │ │ ├── M0040_AddUniqueConstraintToChapterTable.kt │ │ │ │ │ ├── M0041_FixDownloadedChaptersWithoutPageCount.kt │ │ │ │ │ ├── M0042_MangaRemoveLengthLimit_II.kt │ │ │ │ │ ├── M0043_PreventNegativeLastPageRead.kt │ │ │ │ │ ├── M0044_FixDownloadedChaptersWithoutPageCountII.kt │ │ │ │ │ ├── M0045_PreventDuplicatedChapterPages.kt │ │ │ │ │ └── M0046_RenamePageImageUrlColumn.kt │ │ │ └── util │ │ │ │ ├── AppExit.kt │ │ │ │ ├── AppMutex.kt │ │ │ │ ├── Browser.kt │ │ │ │ ├── DocumentationDsl.kt │ │ │ │ ├── SystemTray.kt │ │ │ │ └── WebInterfaceManager.kt │ │ │ └── util │ │ │ └── HAScheduler.kt │ └── resources │ │ ├── graphql-playground.html │ │ ├── icon │ │ ├── faviconlogo-128.png │ │ ├── faviconlogo.ico │ │ ├── faviconlogo.png │ │ └── localSource.png │ │ ├── server-reference.conf │ │ └── static │ │ └── tracker │ │ ├── anilist.png │ │ ├── bangumi.png │ │ ├── kitsu.png │ │ ├── mal.png │ │ └── manga_updates.png │ └── test │ ├── kotlin │ ├── masstest │ │ ├── CloudFlareTest.kt │ │ └── TestExtensionCompatibility.kt │ └── suwayomi │ │ └── tachidesk │ │ ├── ImageUtilTest.kt │ │ ├── PathTest.kt │ │ ├── graphql │ │ └── RequestParserTest.kt │ │ ├── manga │ │ ├── controller │ │ │ ├── CategoryControllerTest.kt │ │ │ └── UpdateControllerTest.kt │ │ ├── impl │ │ │ ├── CategoryMangaTest.kt │ │ │ ├── MangaTest.kt │ │ │ ├── PageTest.kt │ │ │ ├── SearchTest.kt │ │ │ └── update │ │ │ │ └── TestUpdater.kt │ │ └── model │ │ │ └── PaginatedListTest.kt │ │ └── test │ │ ├── ApplicationTest.kt │ │ └── TestUtils.kt │ └── resources │ ├── dice.jxl │ ├── fox.profile0.8bpc.yuv420.avif │ ├── sample1.heif │ └── server-reference.conf └── settings.gradle.kts /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{kt,kts}] 2 | indent_size=4 3 | insert_final_newline=true 4 | ij_kotlin_allow_trailing_comma=true 5 | ij_kotlin_allow_trailing_comma_on_call_site=true 6 | ij_kotlin_name_count_to_use_star_import=2147483647 7 | ij_kotlin_name_count_to_use_star_import_for_members=2147483647 8 | 9 | ktlint_standard_discouraged-comment-location=disabled 10 | ktlint_standard_if-else-wrapping=disabled 11 | ktlint_standard_no-consecutive-comments=disabled -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | * text eol=lf 3 | 4 | # Windows forced line-endings 5 | /.idea/* text eol=crlf 6 | *.bat text eol=crlf 7 | *.ps1 text eol=crlf 8 | 9 | # Gradle wrapper 10 | *.jar binary 11 | 12 | # Binary files types 13 | *.webp binary 14 | *.png binary 15 | *.jpg binary 16 | *.jpeg binary 17 | *.gif binary 18 | *.ico binary 19 | *.gz binary 20 | *.zip binary 21 | *.7z binary 22 | *.ttf binary 23 | *.eot binary 24 | *.woff binary 25 | *.pyc binary 26 | *.swp binary 27 | *.pdf binary 28 | *.exe binary 29 | *.avif binary 30 | *.heif binary 31 | *.jxl binary -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: ☎️ Support 4 | url: https://discord.gg/DDZdqZWaHA 5 | about: Join our discord to get help for anything that is not a bug or a feature request 6 | -------------------------------------------------------------------------------- /.github/runner-files/ci-gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.daemon=false 2 | org.gradle.jvmargs=-Xmx5120m 3 | org.gradle.workers.max=5 4 | org.gradle.parallel=true 5 | 6 | kotlin.incremental=false 7 | kotlin.compiler.execution.strategy=in-process 8 | -------------------------------------------------------------------------------- /.github/workflows/docker_build_stable.yml: -------------------------------------------------------------------------------- 1 | name: Docker Build Stable 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | build_publish_docker_container: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: run docker build and publish script 13 | run: | 14 | curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: token ${{ secrets.DEPLOY_PREVIEW_TOKEN }}" -d '{"ref":"main", "inputs":{"tachidesk_release_type": "stable"}}' https://api.github.com/repos/suwayomi/docker-tachidesk/actions/workflows/build_container_images.yml/dispatches 15 | 16 | -------------------------------------------------------------------------------- /.github/workflows/winget.yml: -------------------------------------------------------------------------------- 1 | name: Publish to WinGet 2 | on: 3 | workflow_run: 4 | workflows: ["CI Publish"] 5 | types: 6 | - completed 7 | jobs: 8 | publish: 9 | runs-on: windows-latest # action can only be run on windows 10 | steps: 11 | - uses: vedantmgoyal2009/winget-releaser@v2 12 | with: 13 | identifier: Suwayomi.Tachidesk-Server 14 | installers-regex: '.*x64.msi$' 15 | token: ${{ secrets.WINGET_PUBLISH_PAT }} 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore project-specific local files and dirs 2 | .gradle 3 | .idea 4 | gradle.properties 5 | .fleet 6 | # But we need these 7 | !.idea/runConfigurations 8 | .kotlin 9 | 10 | # Ignore Gradle build output directory 11 | build 12 | server/out 13 | AndroidCompat/out 14 | 15 | # WebUI is either to be downloaded on-demand or is a dynamic build asset 16 | server/src/main/resources/WebUI.zip 17 | 18 | # bundling stage downlaoded assets 19 | scripts/OpenJDK* 20 | scripts/zulu* 21 | scripts/electron-* 22 | scripts/rcedit-* 23 | -------------------------------------------------------------------------------- /AndroidCompat/Config/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id( 3 | libs.plugins.kotlin.jvm 4 | .get() 5 | .pluginId, 6 | ) 7 | id( 8 | libs.plugins.kotlin.serialization 9 | .get() 10 | .pluginId, 11 | ) 12 | id( 13 | libs.plugins.ktlint 14 | .get() 15 | .pluginId, 16 | ) 17 | } 18 | 19 | dependencies { 20 | // Shared 21 | implementation(libs.bundles.shared) 22 | testImplementation(libs.bundles.sharedTest) 23 | } 24 | -------------------------------------------------------------------------------- /AndroidCompat/Config/src/main/java/xyz/nulldev/ts/config/ApplicationRootDir.kt: -------------------------------------------------------------------------------- 1 | package xyz.nulldev.ts.config 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import net.harawata.appdirs.AppDirsFactory 11 | 12 | const val CONFIG_PREFIX = "suwayomi.tachidesk.config" 13 | 14 | val ApplicationRootDir: String 15 | get(): String { 16 | return System.getProperty( 17 | "$CONFIG_PREFIX.server.rootDir", 18 | AppDirsFactory.getInstance().getUserDataDir("Tachidesk", null, null), 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /AndroidCompat/Config/src/main/java/xyz/nulldev/ts/config/ConfigManagerModule.kt: -------------------------------------------------------------------------------- 1 | package xyz.nulldev.ts.config 2 | 3 | import org.koin.core.module.Module 4 | import org.koin.dsl.module 5 | 6 | fun configManagerModule(): Module = 7 | module { 8 | single { GlobalConfigManager } 9 | } 10 | -------------------------------------------------------------------------------- /AndroidCompat/Config/src/main/java/xyz/nulldev/ts/config/util/ConfigExtensions.kt: -------------------------------------------------------------------------------- 1 | package xyz.nulldev.ts.config.util 2 | 3 | import com.typesafe.config.Config 4 | 5 | operator fun Config.get(key: String) = 6 | getString(key) 7 | ?: throw IllegalStateException("Could not find value for config entry: $key!") 8 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/annotation/AppIdInt.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.annotation; 18 | 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.Target; 21 | 22 | import static java.lang.annotation.ElementType.*; 23 | import static java.lang.annotation.RetentionPolicy.SOURCE; 24 | 25 | /** 26 | * Denotes that the annotated element is a multi-user application ID. This is 27 | * not the same as a UID. 28 | * 29 | * @hide 30 | */ 31 | @Retention(SOURCE) 32 | @Target({METHOD, PARAMETER, FIELD}) 33 | public @interface AppIdInt { 34 | } 35 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/annotation/BoolRes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.annotation; 17 | 18 | import java.lang.annotation.Documented; 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.Target; 21 | 22 | import static java.lang.annotation.ElementType.*; 23 | import static java.lang.annotation.RetentionPolicy.SOURCE; 24 | 25 | /** 26 | * Denotes that an integer parameter, field or method return value is expected 27 | * to be a boolean resource reference. 28 | * 29 | * {@hide} 30 | */ 31 | @Documented 32 | @Retention(SOURCE) 33 | @Target({METHOD, PARAMETER, FIELD}) 34 | public @interface BoolRes { 35 | } 36 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/annotation/MenuRes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.annotation; 17 | 18 | import java.lang.annotation.Documented; 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.Target; 21 | 22 | import static java.lang.annotation.ElementType.*; 23 | import static java.lang.annotation.RetentionPolicy.SOURCE; 24 | 25 | /** 26 | * Denotes that an integer parameter, field or method return value is expected 27 | * to be a menu resource reference. 28 | * 29 | * {@hide} 30 | */ 31 | @Documented 32 | @Retention(SOURCE) 33 | @Target({METHOD, PARAMETER, FIELD}) 34 | public @interface MenuRes { 35 | } 36 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/annotation/NonNull.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.annotation; 17 | 18 | import java.lang.annotation.Retention; 19 | import java.lang.annotation.Target; 20 | 21 | import static java.lang.annotation.ElementType.*; 22 | import static java.lang.annotation.RetentionPolicy.SOURCE; 23 | 24 | /** 25 | * Denotes that a parameter, field or method return value can never be null. 26 | *

27 | * This is a marker annotation and it has no specific attributes. 28 | * 29 | * @hide 30 | */ 31 | @Retention(SOURCE) 32 | @Target({METHOD, PARAMETER, FIELD}) 33 | public @interface NonNull { 34 | } 35 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/annotation/PluralsRes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.annotation; 17 | 18 | import java.lang.annotation.Documented; 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.Target; 21 | 22 | import static java.lang.annotation.ElementType.*; 23 | import static java.lang.annotation.RetentionPolicy.SOURCE; 24 | 25 | /** 26 | * Denotes that an integer parameter, field or method return value is expected 27 | * to be a plurals resource reference. 28 | * 29 | * {@hide} 30 | */ 31 | @Documented 32 | @Retention(SOURCE) 33 | @Target({METHOD, PARAMETER, FIELD}) 34 | public @interface PluralsRes { 35 | } 36 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/annotation/RawRes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.annotation; 17 | 18 | import java.lang.annotation.Documented; 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.Target; 21 | 22 | import static java.lang.annotation.ElementType.*; 23 | import static java.lang.annotation.RetentionPolicy.SOURCE; 24 | 25 | /** 26 | * Denotes that an integer parameter, field or method return value is expected 27 | * to be a raw resource reference. 28 | * 29 | * {@hide} 30 | */ 31 | @Documented 32 | @Retention(SOURCE) 33 | @Target({METHOD, PARAMETER, FIELD}) 34 | public @interface RawRes { 35 | } 36 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/annotation/UserIdInt.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.annotation; 18 | 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.Target; 21 | 22 | import static java.lang.annotation.ElementType.*; 23 | import static java.lang.annotation.RetentionPolicy.SOURCE; 24 | 25 | /** 26 | * Denotes that the annotated element is a multi-user user ID. This is 27 | * not the same as a UID. 28 | * 29 | * @hide 30 | */ 31 | @Retention(SOURCE) 32 | @Target({METHOD, PARAMETER, FIELD}) 33 | public @interface UserIdInt { 34 | } 35 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/annotation/XmlRes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.annotation; 17 | 18 | import java.lang.annotation.Documented; 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.Target; 21 | 22 | import static java.lang.annotation.ElementType.*; 23 | import static java.lang.annotation.RetentionPolicy.SOURCE; 24 | 25 | /** 26 | * Denotes that an integer parameter, field or method return value is expected 27 | * to be an XML resource reference. 28 | * 29 | * {@hide} 30 | */ 31 | @Documented 32 | @Retention(SOURCE) 33 | @Target({METHOD, PARAMETER, FIELD}) 34 | public @interface XmlRes { 35 | } 36 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/app/PackageDeleteObserver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.app; 17 | import android.content.Intent; 18 | /** {@hide} */ 19 | public class PackageDeleteObserver { 20 | public void onUserActionRequired(Intent intent) { 21 | } 22 | public void onPackageDeleted(String basePackageName, int returnCode, String msg) { 23 | } 24 | } -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/content/pm/PackageParser.java: -------------------------------------------------------------------------------- 1 | package android.content.pm; 2 | 3 | public class PackageParser { 4 | public static class PackageParserException extends Exception { 5 | public final int error; 6 | public PackageParserException(int error, String detailMessage) { 7 | super(detailMessage); 8 | this.error = error; 9 | } 10 | public PackageParserException(int error, String detailMessage, Throwable throwable) { 11 | super(detailMessage, throwable); 12 | this.error = error; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/database/CharArrayBuffer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.database; 17 | /** 18 | * This is used for {@link Cursor#copyStringToBuffer} 19 | */ 20 | public final class CharArrayBuffer { 21 | public CharArrayBuffer(int size) { 22 | data = new char[size]; 23 | } 24 | 25 | public CharArrayBuffer(char[] buf) { 26 | data = buf; 27 | } 28 | 29 | public char[] data; // In and out parameter 30 | public int sizeCopied; // Out parameter 31 | } -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/database/SQLException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.database; 17 | /** 18 | * An exception that indicates there was an error with SQL parsing or execution. 19 | */ 20 | public class SQLException extends RuntimeException { 21 | public SQLException() { 22 | } 23 | public SQLException(String error) { 24 | super(error); 25 | } 26 | public SQLException(String error, Throwable cause) { 27 | super(error, cause); 28 | } 29 | } -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/database/sqlite/DatabaseObjectNotClosedException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.database.sqlite; 18 | 19 | /** 20 | * An exception that indicates that garbage-collector is finalizing a database object 21 | * that is not explicitly closed 22 | * @hide 23 | */ 24 | public class DatabaseObjectNotClosedException extends RuntimeException { 25 | private static final String s = "Application did not close the cursor or database object " + 26 | "that was opened here"; 27 | 28 | public DatabaseObjectNotClosedException() { 29 | super(s); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/database/sqlite/SQLiteAbortException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.database.sqlite; 18 | 19 | /** 20 | * An exception that indicates that the SQLite program was aborted. 21 | * This can happen either through a call to ABORT in a trigger, 22 | * or as the result of using the ABORT conflict clause. 23 | */ 24 | public class SQLiteAbortException extends SQLiteException { 25 | public SQLiteAbortException() {} 26 | 27 | public SQLiteAbortException(String error) { 28 | super(error); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/database/sqlite/SQLiteAccessPermException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.database.sqlite; 18 | 19 | /** 20 | * This exception class is used when sqlite can't access the database file 21 | * due to lack of permissions on the file. 22 | */ 23 | public class SQLiteAccessPermException extends SQLiteException { 24 | public SQLiteAccessPermException() {} 25 | 26 | public SQLiteAccessPermException(String error) { 27 | super(error); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.database.sqlite; 18 | 19 | /** 20 | * Thrown if the the bind or column parameter index is out of range 21 | */ 22 | public class SQLiteBindOrColumnIndexOutOfRangeException extends SQLiteException { 23 | public SQLiteBindOrColumnIndexOutOfRangeException() {} 24 | 25 | public SQLiteBindOrColumnIndexOutOfRangeException(String error) { 26 | super(error); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/database/sqlite/SQLiteBlobTooBigException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.database.sqlite; 18 | 19 | public class SQLiteBlobTooBigException extends SQLiteException { 20 | public SQLiteBlobTooBigException() {} 21 | 22 | public SQLiteBlobTooBigException(String error) { 23 | super(error); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/database/sqlite/SQLiteCantOpenDatabaseException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.database.sqlite; 18 | 19 | public class SQLiteCantOpenDatabaseException extends SQLiteException { 20 | public SQLiteCantOpenDatabaseException() {} 21 | 22 | public SQLiteCantOpenDatabaseException(String error) { 23 | super(error); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/database/sqlite/SQLiteConstraintException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.database.sqlite; 18 | 19 | /** 20 | * An exception that indicates that an integrity constraint was violated. 21 | */ 22 | public class SQLiteConstraintException extends SQLiteException { 23 | public SQLiteConstraintException() {} 24 | 25 | public SQLiteConstraintException(String error) { 26 | super(error); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/database/sqlite/SQLiteDatabaseCorruptException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.database.sqlite; 18 | 19 | /** 20 | * An exception that indicates that the SQLite database file is corrupt. 21 | */ 22 | public class SQLiteDatabaseCorruptException extends SQLiteException { 23 | public SQLiteDatabaseCorruptException() {} 24 | 25 | public SQLiteDatabaseCorruptException(String error) { 26 | super(error); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/database/sqlite/SQLiteDatatypeMismatchException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.database.sqlite; 18 | 19 | public class SQLiteDatatypeMismatchException extends SQLiteException { 20 | public SQLiteDatatypeMismatchException() {} 21 | 22 | public SQLiteDatatypeMismatchException(String error) { 23 | super(error); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/database/sqlite/SQLiteDiskIOException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.database.sqlite; 18 | 19 | /** 20 | * An exception that indicates that an IO error occured while accessing the 21 | * SQLite database file. 22 | */ 23 | public class SQLiteDiskIOException extends SQLiteException { 24 | public SQLiteDiskIOException() {} 25 | 26 | public SQLiteDiskIOException(String error) { 27 | super(error); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/database/sqlite/SQLiteDoneException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.database.sqlite; 18 | 19 | /** 20 | * An exception that indicates that the SQLite program is done. 21 | * Thrown when an operation that expects a row (such as {@link 22 | * SQLiteStatement#simpleQueryForString} or {@link 23 | * SQLiteStatement#simpleQueryForLong}) does not get one. 24 | */ 25 | public class SQLiteDoneException extends SQLiteException { 26 | public SQLiteDoneException() {} 27 | 28 | public SQLiteDoneException(String error) { 29 | super(error); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/database/sqlite/SQLiteException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.database.sqlite; 18 | 19 | import android.database.SQLException; 20 | 21 | /** 22 | * A SQLite exception that indicates there was an error with SQL parsing or execution. 23 | */ 24 | public class SQLiteException extends SQLException { 25 | public SQLiteException() { 26 | } 27 | 28 | public SQLiteException(String error) { 29 | super(error); 30 | } 31 | 32 | public SQLiteException(String error, Throwable cause) { 33 | super(error, cause); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/database/sqlite/SQLiteFullException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.database.sqlite; 18 | 19 | /** 20 | * An exception that indicates that the SQLite database is full. 21 | */ 22 | public class SQLiteFullException extends SQLiteException { 23 | public SQLiteFullException() {} 24 | 25 | public SQLiteFullException(String error) { 26 | super(error); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/database/sqlite/SQLiteOutOfMemoryException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.database.sqlite; 18 | 19 | public class SQLiteOutOfMemoryException extends SQLiteException { 20 | public SQLiteOutOfMemoryException() {} 21 | 22 | public SQLiteOutOfMemoryException(String error) { 23 | super(error); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/database/sqlite/SQLiteReadOnlyDatabaseException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.database.sqlite; 18 | 19 | public class SQLiteReadOnlyDatabaseException extends SQLiteException { 20 | public SQLiteReadOnlyDatabaseException() {} 21 | 22 | public SQLiteReadOnlyDatabaseException(String error) { 23 | super(error); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/database/sqlite/SQLiteStatementInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.database.sqlite; 18 | 19 | /** 20 | * Describes a SQLite statement. 21 | * 22 | * @hide 23 | */ 24 | public final class SQLiteStatementInfo { 25 | /** 26 | * The number of parameters that the statement has. 27 | */ 28 | public int numParameters; 29 | 30 | /** 31 | * The names of all columns in the result set of the statement. 32 | */ 33 | public String[] columnNames; 34 | 35 | /** 36 | * True if the statement is read-only. 37 | */ 38 | public boolean readOnly; 39 | } 40 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/database/sqlite/SQLiteTableLockedException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.database.sqlite; 18 | 19 | public class SQLiteTableLockedException extends SQLiteException { 20 | public SQLiteTableLockedException() {} 21 | 22 | public SQLiteTableLockedException(String error) { 23 | super(error); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/database/sqlite/SQLiteTransactionListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.database.sqlite; 18 | 19 | /** 20 | * A listener for transaction events. 21 | */ 22 | public interface SQLiteTransactionListener { 23 | /** 24 | * Called immediately after the transaction begins. 25 | */ 26 | void onBegin(); 27 | 28 | /** 29 | * Called immediately before commiting the transaction. 30 | */ 31 | void onCommit(); 32 | 33 | /** 34 | * Called if the transaction is about to be rolled back. 35 | */ 36 | void onRollback(); 37 | } 38 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/graphics/Canvas.java: -------------------------------------------------------------------------------- 1 | package android.graphics; 2 | 3 | import java.awt.Graphics2D; 4 | import java.awt.image.BufferedImage; 5 | import javax.imageio.ImageIO; 6 | 7 | public final class Canvas { 8 | private BufferedImage canvasImage; 9 | private Graphics2D canvas; 10 | 11 | public Canvas(Bitmap bitmap) { 12 | canvasImage = bitmap.getImage(); 13 | canvas = canvasImage.createGraphics(); 14 | } 15 | 16 | public void drawBitmap(Bitmap sourceBitmap, Rect src, Rect dst, Paint paint) { 17 | BufferedImage sourceImage = sourceBitmap.getImage(); 18 | BufferedImage sourceImageCropped = sourceImage.getSubimage(src.left, src.top, src.getWidth(), src.getHeight()); 19 | canvas.drawImage(sourceImageCropped, null, dst.left, dst.top); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/net/ParseException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.net; 17 | /** 18 | * Thrown when parsing a URL fails. 19 | */ 20 | // See non-public class {@link WebAddress}. 21 | public class ParseException extends RuntimeException { 22 | public String response; 23 | ParseException(String response) { 24 | this.response = response; 25 | } 26 | } -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/preference/PreferenceManager.kt: -------------------------------------------------------------------------------- 1 | package android.preference 2 | 3 | import android.content.Context 4 | 5 | /** 6 | * Created by nulldev on 3/26/17. 7 | */ 8 | 9 | class PreferenceManager { 10 | companion object { 11 | @JvmStatic 12 | fun getDefaultSharedPreferences(context: Context) = 13 | context.getSharedPreferences( 14 | context.applicationInfo.packageName, 15 | Context.MODE_PRIVATE, 16 | )!! 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/support/annotation/BoolRes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.support.annotation; 17 | 18 | import java.lang.annotation.Documented; 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.Target; 21 | 22 | import static java.lang.annotation.ElementType.*; 23 | import static java.lang.annotation.RetentionPolicy.CLASS; 24 | 25 | /** 26 | * Denotes that an integer parameter, field or method return value is expected 27 | * to be a boolean resource reference. 28 | */ 29 | @Documented 30 | @Retention(CLASS) 31 | @Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE}) 32 | public @interface BoolRes { 33 | } 34 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/support/annotation/RawRes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.support.annotation; 17 | 18 | import java.lang.annotation.Documented; 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.Target; 21 | 22 | import static java.lang.annotation.ElementType.*; 23 | import static java.lang.annotation.RetentionPolicy.CLASS; 24 | 25 | /** 26 | * Denotes that an integer parameter, field or method return value is expected 27 | * to be a raw resource reference. 28 | */ 29 | @Documented 30 | @Retention(CLASS) 31 | @Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE}) 32 | public @interface RawRes { 33 | } 34 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/support/annotation/VisibleForTesting.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.support.annotation; 17 | 18 | import java.lang.annotation.Retention; 19 | 20 | import static java.lang.annotation.RetentionPolicy.CLASS; 21 | 22 | /** 23 | * Denotes that the class, method or field has its visibility relaxed, so that it is more widely 24 | * visible than otherwise necessary to make code testable. 25 | */ 26 | @Retention(CLASS) 27 | public @interface VisibleForTesting { 28 | } 29 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/support/annotation/XmlRes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.support.annotation; 17 | 18 | import java.lang.annotation.Documented; 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.Target; 21 | 22 | import static java.lang.annotation.ElementType.*; 23 | import static java.lang.annotation.RetentionPolicy.CLASS; 24 | 25 | /** 26 | * Denotes that an integer parameter, field or method return value is expected 27 | * to be an XML resource reference. 28 | */ 29 | @Documented 30 | @Retention(CLASS) 31 | @Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE}) 32 | public @interface XmlRes { 33 | } 34 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/support/annotation/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * All of the files in this package were copied unchanged from the Android Open Source Project (AOSP) 3 | */ 4 | package android.support.annotation; -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/support/multidex/MultiDex.java: -------------------------------------------------------------------------------- 1 | package android.support.multidex; 2 | 3 | import android.content.Context; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | /** 8 | * MultiDex that does nothing. 9 | */ 10 | public class MultiDex { 11 | private static Logger logger = LoggerFactory.getLogger(MultiDex.class); 12 | 13 | public static void install(Context context) { 14 | logger.debug("Ignoring MultiDex installation attempt for app: {}", context.getPackageName()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/android/text/format/Formatter.java: -------------------------------------------------------------------------------- 1 | package android.text.format; 2 | 3 | import android.content.Context; 4 | 5 | import java.text.DecimalFormat; 6 | 7 | /** 8 | * Custom reimplementation of some of the methods used in Android. 9 | */ 10 | public class Formatter { 11 | private Formatter() { 12 | throw new RuntimeException("Stub!"); 13 | } 14 | 15 | public static String formatFileSize(Context context, long size) { 16 | if(size <= 0) return "0"; 17 | final String[] units = new String[] { "B", "KB", "MB", "GB", "TB", "PB", "EB" }; 18 | int digitGroups = (int) (Math.log10(size)/Math.log10(1024)); 19 | return new DecimalFormat("#,##0.#").format(size/Math.pow(1024, digitGroups)) + " " + units[digitGroups]; 20 | } 21 | 22 | public static String formatShortFileSize(Context context, long sizeBytes) { 23 | return formatFileSize(context, sizeBytes); 24 | } 25 | 26 | /** @deprecated */ 27 | @Deprecated 28 | public static String formatIpAddress(int ipv4Address) { 29 | throw new RuntimeException("Stub!"); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/androidx/preference/CheckBoxPreference.java: -------------------------------------------------------------------------------- 1 | package androidx.preference; 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import android.content.Context; 11 | 12 | public class CheckBoxPreference extends TwoStatePreference { 13 | // reference: https://android.googlesource.com/platform/frameworks/support/+/996971f962fcd554339a7cb2859cef9ca89dbcb7/preference/preference/src/main/java/androidx/preference/CheckBoxPreference.java 14 | 15 | public CheckBoxPreference(Context context) { 16 | super(context); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/androidx/preference/DialogPreference.java: -------------------------------------------------------------------------------- 1 | package androidx.preference; 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import android.content.Context; 11 | 12 | public abstract class DialogPreference extends Preference { 13 | private CharSequence dialogTitle; 14 | private CharSequence dialogMessage; 15 | 16 | public DialogPreference(Context context) { super(context); } 17 | 18 | public CharSequence getDialogTitle() { 19 | return dialogTitle; 20 | } 21 | 22 | public void setDialogTitle(CharSequence dialogTitle) { 23 | this.dialogTitle = dialogTitle; 24 | } 25 | 26 | public CharSequence getDialogMessage() { 27 | return dialogMessage; 28 | } 29 | 30 | public void setDialogMessage(CharSequence dialogMessage) { 31 | this.dialogMessage = dialogMessage; 32 | } 33 | } -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/androidx/preference/PreferenceScreen.java: -------------------------------------------------------------------------------- 1 | package androidx.preference; 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import android.content.Context; 11 | 12 | import java.util.LinkedList; 13 | import java.util.List; 14 | 15 | public class PreferenceScreen extends Preference { 16 | /** Tachidesk specific API */ 17 | private List preferences = new LinkedList<>(); 18 | 19 | public PreferenceScreen(Context context) { 20 | super(context); 21 | } 22 | 23 | public boolean addPreference(Preference preference) { 24 | // propagate own shared preferences 25 | preference.setSharedPreferences(getSharedPreferences()); 26 | 27 | preferences.add(preference); 28 | 29 | return true; 30 | } 31 | 32 | /** Tachidesk specific API */ 33 | public List getPreferences(){ 34 | return preferences; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/androidx/preference/SwitchPreferenceCompat.java: -------------------------------------------------------------------------------- 1 | package androidx.preference; 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import android.content.Context; 11 | 12 | public class SwitchPreferenceCompat extends TwoStatePreference { 13 | // reference: https://android.googlesource.com/platform/frameworks/support/+/996971f962fcd554339a7cb2859cef9ca89dbcb7/preference/preference/src/main/java/androidx/preference/CheckBoxPreference.java 14 | 15 | public SwitchPreferenceCompat(Context context) { 16 | super(context); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/app/cash/quickjs/QuickJsException.java: -------------------------------------------------------------------------------- 1 | package app.cash.quickjs; 2 | 3 | public final class QuickJsException extends RuntimeException { 4 | public QuickJsException(String message, Throwable cause) { 5 | super(message, cause); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/com/squareup/duktape/DuktapeStub.java: -------------------------------------------------------------------------------- 1 | package com.squareup.duktape; 2 | 3 | /* part of tachiyomi-extensions which is licensed under Apache License Version 2.0 */ 4 | 5 | import java.io.Closeable; 6 | import java.io.IOException; 7 | 8 | /** This is the reference Duktape stub that tachiyomi's extensions depend on. 9 | * Intended to be used as a reference. 10 | */ 11 | public class DuktapeStub implements Closeable { 12 | 13 | public static Duktape create() { 14 | throw new RuntimeException("Stub!"); 15 | } 16 | 17 | @Override 18 | public synchronized void close() throws IOException { 19 | throw new RuntimeException("Stub!"); 20 | } 21 | 22 | public synchronized Object evaluate(String script) { 23 | throw new RuntimeException("Stub!"); 24 | } 25 | 26 | public synchronized void set(String name, Class type, T object) { 27 | throw new RuntimeException("Stub!"); 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/io/requery/android/database/sqlite/RequerySQLiteOpenHelperFactory.kt: -------------------------------------------------------------------------------- 1 | package io.requery.android.database.sqlite 2 | 3 | import android.arch.persistence.db.SupportSQLiteOpenHelper 4 | import android.arch.persistence.db.framework.FrameworkSQLiteOpenHelperFactory 5 | 6 | class RequerySQLiteOpenHelperFactory { 7 | fun create(configuration: SupportSQLiteOpenHelper.Configuration) = FrameworkSQLiteOpenHelperFactory().create(configuration) 8 | } 9 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/org/json/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package copied and modified from: https://android.googlesource.com/platform/libcore/+/master/json 3 | */ 4 | package org.json; -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/rx.android.schedulers/AndroidSchedulers.kt: -------------------------------------------------------------------------------- 1 | package rx.android.schedulers 2 | 3 | import rx.Scheduler 4 | import rx.internal.schedulers.ImmediateScheduler 5 | 6 | class AndroidSchedulers { 7 | companion object { 8 | val mainThreadScheduler by lazy { 9 | ImmediateScheduler.INSTANCE!! 10 | } 11 | 12 | /** 13 | * Simulated main thread scheduler 14 | */ 15 | @JvmStatic 16 | fun mainThread(): Scheduler = mainThreadScheduler 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/xyz/nulldev/androidcompat/AndroidCompat.kt: -------------------------------------------------------------------------------- 1 | package xyz.nulldev.androidcompat 2 | 3 | import android.app.Application 4 | import org.koin.mp.KoinPlatformTools 5 | import xyz.nulldev.androidcompat.androidimpl.CustomContext 6 | 7 | class AndroidCompat { 8 | val context: CustomContext by KoinPlatformTools.defaultContext().get().inject() 9 | 10 | fun startApp(application: Application) { 11 | application.attach(context) 12 | application.onCreate() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/xyz/nulldev/androidcompat/AndroidCompatInitializer.kt: -------------------------------------------------------------------------------- 1 | package xyz.nulldev.androidcompat 2 | 3 | import xyz.nulldev.androidcompat.config.ApplicationInfoConfigModule 4 | import xyz.nulldev.androidcompat.config.FilesConfigModule 5 | import xyz.nulldev.androidcompat.config.SystemConfigModule 6 | import xyz.nulldev.ts.config.GlobalConfigManager 7 | 8 | /** 9 | * Initializes the Android compatibility module 10 | */ 11 | class AndroidCompatInitializer { 12 | fun init() { 13 | // Register config modules 14 | GlobalConfigManager.registerModules( 15 | FilesConfigModule.register(GlobalConfigManager.config), 16 | ApplicationInfoConfigModule.register(GlobalConfigManager.config), 17 | SystemConfigModule.register(GlobalConfigManager.config), 18 | ) 19 | 20 | // Set some properties extensions use 21 | System.setProperty( 22 | "http.agent", 23 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/xyz/nulldev/androidcompat/AndroidCompatModule.kt: -------------------------------------------------------------------------------- 1 | package xyz.nulldev.androidcompat 2 | 3 | import android.content.Context 4 | import org.koin.core.module.Module 5 | import org.koin.dsl.module 6 | import xyz.nulldev.androidcompat.androidimpl.CustomContext 7 | import xyz.nulldev.androidcompat.androidimpl.FakePackageManager 8 | import xyz.nulldev.androidcompat.info.ApplicationInfoImpl 9 | import xyz.nulldev.androidcompat.io.AndroidFiles 10 | import xyz.nulldev.androidcompat.pm.PackageController 11 | import xyz.nulldev.androidcompat.service.ServiceSupport 12 | 13 | /** 14 | * AndroidCompatModule 15 | */ 16 | 17 | fun androidCompatModule(): Module = 18 | module { 19 | single { AndroidFiles() } 20 | 21 | single { ApplicationInfoImpl(get()) } 22 | 23 | single { ServiceSupport() } 24 | 25 | single { FakePackageManager() } 26 | 27 | single { PackageController() } 28 | 29 | single { CustomContext() } 30 | 31 | single { get() } 32 | } 33 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/xyz/nulldev/androidcompat/config/ApplicationInfoConfigModule.kt: -------------------------------------------------------------------------------- 1 | package xyz.nulldev.androidcompat.config 2 | 3 | import com.typesafe.config.Config 4 | import io.github.config4k.getValue 5 | import xyz.nulldev.ts.config.ConfigModule 6 | 7 | /** 8 | * Application info config. 9 | */ 10 | 11 | class ApplicationInfoConfigModule( 12 | getConfig: () -> Config, 13 | ) : ConfigModule(getConfig) { 14 | val packageName: String by getConfig() 15 | val debug: Boolean by getConfig() 16 | 17 | companion object { 18 | fun register(config: Config) = ApplicationInfoConfigModule { config.getConfig("android.app") } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/xyz/nulldev/androidcompat/config/SystemConfigModule.kt: -------------------------------------------------------------------------------- 1 | package xyz.nulldev.androidcompat.config 2 | 3 | import com.typesafe.config.Config 4 | import io.github.config4k.getValue 5 | import xyz.nulldev.ts.config.ConfigModule 6 | 7 | class SystemConfigModule( 8 | val getConfig: () -> Config, 9 | ) : ConfigModule(getConfig) { 10 | val isDebuggable: Boolean by getConfig() 11 | 12 | val propertyPrefix = "properties." 13 | 14 | fun getStringProperty(property: String) = getConfig().getString("$propertyPrefix$property")!! 15 | 16 | fun getIntProperty(property: String) = getConfig().getInt("$propertyPrefix$property") 17 | 18 | fun getLongProperty(property: String) = getConfig().getLong("$propertyPrefix$property") 19 | 20 | fun getBooleanProperty(property: String) = getConfig().getBoolean("$propertyPrefix$property") 21 | 22 | fun hasProperty(property: String) = getConfig().hasPath("$propertyPrefix$property") 23 | 24 | companion object { 25 | fun register(config: Config) = SystemConfigModule { config.getConfig("android.system") } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/xyz/nulldev/androidcompat/info/ApplicationInfoImpl.kt: -------------------------------------------------------------------------------- 1 | package xyz.nulldev.androidcompat.info 2 | 3 | import android.content.pm.ApplicationInfo 4 | import xyz.nulldev.androidcompat.config.ApplicationInfoConfigModule 5 | import xyz.nulldev.ts.config.ConfigManager 6 | 7 | class ApplicationInfoImpl( 8 | private val configManager: ConfigManager, 9 | ) : ApplicationInfo() { 10 | val appInfoConfig: ApplicationInfoConfigModule 11 | get() = configManager.module() 12 | 13 | val debug: Boolean get() = appInfoConfig.debug 14 | 15 | init { 16 | super.packageName = appInfoConfig.packageName 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/xyz/nulldev/androidcompat/pm/PackageUtil.kt: -------------------------------------------------------------------------------- 1 | package xyz.nulldev.androidcompat.pm 2 | 3 | import android.content.pm.ApplicationInfo 4 | import android.content.pm.FeatureInfo 5 | import android.content.pm.PackageInfo 6 | import net.dongliu.apk.parser.bean.ApkMeta 7 | import java.io.File 8 | 9 | fun ApkMeta.toPackageInfo(apk: File): PackageInfo = 10 | PackageInfo().also { 11 | it.packageName = packageName 12 | it.versionCode = versionCode.toInt() 13 | it.versionName = versionName 14 | 15 | it.reqFeatures = 16 | usesFeatures 17 | .map { 18 | FeatureInfo().apply { 19 | name = it.name 20 | } 21 | }.toTypedArray() 22 | 23 | it.applicationInfo = 24 | ApplicationInfo().apply { 25 | packageName = it.packageName 26 | nonLocalizedLabel = label 27 | sourceDir = apk.absolutePath 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/xyz/nulldev/androidcompat/res/BuildConfigCompat.java: -------------------------------------------------------------------------------- 1 | package xyz.nulldev.androidcompat.res; 2 | 3 | import xyz.nulldev.androidcompat.info.ApplicationInfoImpl; 4 | import xyz.nulldev.androidcompat.util.KoinGlobalHelper; 5 | 6 | import java.text.SimpleDateFormat; 7 | import java.util.Calendar; 8 | 9 | /** 10 | * BuildConfig compat class. 11 | */ 12 | public class BuildConfigCompat { 13 | private static ApplicationInfoImpl applicationInfo = KoinGlobalHelper.instance(ApplicationInfoImpl.class); 14 | 15 | public static final boolean DEBUG = applicationInfo.getDebug(); 16 | 17 | //We assume application ID = package name 18 | public static final String APPLICATION_ID = applicationInfo.packageName; 19 | 20 | //TODO Build time is hardcoded currently 21 | public static final String BUILD_TIME; 22 | static { 23 | Calendar cal = Calendar.getInstance(); 24 | cal.set(2000, Calendar.JANUARY, 1); 25 | BUILD_TIME = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'").format(cal.getTime()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/xyz/nulldev/androidcompat/res/DrawableResource.kt: -------------------------------------------------------------------------------- 1 | package xyz.nulldev.androidcompat.res 2 | 3 | class DrawableResource( 4 | val location: String, 5 | ) : Resource { 6 | override fun getType() = DrawableResource::class.java 7 | 8 | override fun getValue() = javaClass.getResourceAsStream(location) 9 | } 10 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/xyz/nulldev/androidcompat/res/Resource.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Andy Bao 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package xyz.nulldev.androidcompat.res 18 | 19 | /** 20 | * A static Android resource. 21 | */ 22 | 23 | interface Resource { 24 | fun getType(): Class 25 | 26 | fun getValue(): Any? 27 | } 28 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/xyz/nulldev/androidcompat/res/StringResource.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Andy Bao 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package xyz.nulldev.androidcompat.res 18 | 19 | /** 20 | * String resource. 21 | */ 22 | class StringResource( 23 | val string: String, 24 | ) : Resource { 25 | override fun getValue() = string 26 | 27 | override fun getType() = StringResource::class.java 28 | } 29 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/xyz/nulldev/androidcompat/util/KoinGlobalHelper.kt: -------------------------------------------------------------------------------- 1 | package xyz.nulldev.androidcompat.util 2 | 3 | import org.koin.core.Koin 4 | import org.koin.mp.KoinPlatformTools 5 | 6 | /** 7 | * Helper class to allow access to Kodein from Java 8 | */ 9 | object KoinGlobalHelper { 10 | /** 11 | * Get the Kodein object 12 | */ 13 | @JvmStatic 14 | fun koin() = KoinPlatformTools.defaultContext().get() 15 | 16 | /** 17 | * Get a dependency 18 | */ 19 | @JvmStatic 20 | fun instance( 21 | type: Class, 22 | koin: Koin? = null, 23 | ): T = (koin ?: koin()).get(type.kotlin) 24 | 25 | @JvmStatic 26 | fun instance(type: Class): T = instance(type, null) 27 | } 28 | -------------------------------------------------------------------------------- /AndroidCompat/src/main/java/xyz/nulldev/androidcompat/util/UriExtensions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Andy Bao 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package xyz.nulldev.androidcompat.util 18 | 19 | import android.net.Uri 20 | import java.io.File 21 | import java.net.URI 22 | 23 | /** 24 | * Utilites to convert between Java and Android Uris. 25 | */ 26 | fun Uri.java() = URI(this.toString()) 27 | 28 | fun Uri.file() = File(this.path) 29 | 30 | fun URI.android() = Uri.parse(this.toString())!! 31 | -------------------------------------------------------------------------------- /CHANGELOG-TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Server: v0.X.Y-next + WebUI: rXXX 2 | ## TL;DR 3 | - N/A 4 | 5 | ## Suwayomi-Server Changelog 6 | - N/A 7 | 8 | ## Suwayomi-WebUI Changelog 9 | - N/A 10 | 11 | 12 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code Of Conduct 2 | - Don't be a dick. 3 | 4 | # expanding the code of conduct! 5 | The contents of this document is up for debate and improvement! Discussions on discord. 6 | -------------------------------------------------------------------------------- /buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | repositories { 6 | mavenCentral() 7 | } 8 | 9 | dependencies { 10 | implementation(libs.zip4j) 11 | } 12 | -------------------------------------------------------------------------------- /buildSrc/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "buildSrc" 2 | 3 | dependencyResolutionManagement { 4 | versionCatalogs { 5 | create("libs") { 6 | from(files("../gradle/libs.versions.toml")) 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/Constants.kt: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | const val MainClass = "suwayomi.tachidesk.MainKt" 11 | 12 | // should be bumped with each stable release 13 | val getTachideskVersion = { "v2.0.${getCommitCount()}" } 14 | 15 | val webUIRevisionTag = "r2467" 16 | 17 | private val getCommitCount = { 18 | runCatching { 19 | ProcessBuilder() 20 | .command("git", "rev-list", "HEAD", "--count") 21 | .start() 22 | .let { process -> 23 | process.waitFor() 24 | val output = process.inputStream.use { 25 | it.bufferedReader().use(BufferedReader::readText) 26 | } 27 | process.destroy() 28 | output.trim() 29 | } 30 | }.getOrDefault("0") 31 | } 32 | 33 | // counts commits on the current checked out branch 34 | val getTachideskRevision = { "r${getCommitCount()}" } 35 | 36 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Suwayomi-Server/dee61e191c0175c6dae6346f58293e0225df8b1a/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ], 6 | "semanticCommits": "disabled", 7 | "customManagers": [ 8 | { 9 | "customType": "regex", 10 | "fileMatch": [ 11 | "scripts/bundler.sh" 12 | ], 13 | "matchStrings": [ 14 | "JRE_RELEASE=[\"'](?.+?)[\"']\\s+" 15 | ], 16 | "datasourceTemplate": "github-releases", 17 | "depNameTemplate": "adoptium/temurin21-binaries", 18 | "versioningTemplate": "regex:^jdk-?(?\\d+).(?\\d+).+?(?[\\d+]+)$" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /scripts/resources/Suwayomi Launcher.bat: -------------------------------------------------------------------------------- 1 | start "" jre/bin/javaw -jar Suwayomi-Launcher.jar -------------------------------------------------------------------------------- /scripts/resources/Suwayomi Launcher.command: -------------------------------------------------------------------------------- 1 | cd "`dirname "$0"`" 2 | 3 | ./jre/bin/java -jar Suwayomi-Launcher.jar -------------------------------------------------------------------------------- /scripts/resources/deb/changelog: -------------------------------------------------------------------------------- 1 | suwayomi-server ($pkgver-$pkgrel) unstable; urgency=medium 2 | 3 | * See CHANGELOG.md on https://github.com/Suwayomi/Suwayomi-Server 4 | 5 | -- Mahor1221 Fri, 14 Jan 2022 00:00:00 +0000 6 | -------------------------------------------------------------------------------- /scripts/resources/deb/control: -------------------------------------------------------------------------------- 1 | Source: suwayomi-server 2 | Section: web 3 | Priority: optional 4 | Maintainer: Mahor1221 5 | Build-Depends: debhelper-compat (= 13), dh-exec 6 | Standards-Version: 4.5.1 7 | Homepage: https://github.com/Suwayomi/Suwayomi-Server 8 | 9 | Package: suwayomi-server 10 | Architecture: all 11 | Depends: ${misc:Depends}, openjdk-21-jre | openjdk-21-jre-headless | openjdk-21-jdk | openjdk-21-jdk-headless | temurin-21-jre | temurin-21-jdk | zulu21-jre | zulu21-jre-headless | zulu21-jdk | zulu21-jdk-headless | msopenjdk-21 | java-21-amazon-corretto-jdk 12 | Description: Manga Reader 13 | A free and open source manga reader server that runs extensions built for Tachiyomi. 14 | Suwayomi is an independent Tachiyomi compatible software and is not a Fork of Tachiyomi. 15 | -------------------------------------------------------------------------------- /scripts/resources/deb/install: -------------------------------------------------------------------------------- 1 | #!/usr/bin/dh-exec 2 | 3 | Suwayomi-Server.jar usr/share/java/suwayomi-server/bin/ 4 | Suwayomi-Launcher.jar usr/share/java/suwayomi-server/ 5 | suwayomi-server.png usr/share/pixmaps/ 6 | suwayomi-server.desktop usr/share/applications/ 7 | suwayomi-launcher.desktop usr/share/applications/ 8 | suwayomi-server.service usr/lib/systemd/system/ 9 | suwayomi-server.sysusers => usr/lib/sysusers.d/suwayomi-server.conf 10 | suwayomi-server.tmpfiles => usr/lib/tmpfiles.d/suwayomi-server.conf 11 | suwayomi-server.conf => etc/suwayomi/server.conf 12 | suwayomi-server.sh => usr/bin/suwayomi-server 13 | suwayomi-launcher.sh => usr/bin/suwayomi-launcher 14 | -------------------------------------------------------------------------------- /scripts/resources/deb/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # See debhelper(7) (uncomment to enable) 3 | # output every command that modifies files on the build system. 4 | #export DH_VERBOSE = 1 5 | 6 | %: 7 | dh $@ 8 | 9 | override_dh_strip_nondeterminism: 10 | true 11 | 12 | -------------------------------------------------------------------------------- /scripts/resources/deb/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /scripts/resources/deb/source/include-binaries: -------------------------------------------------------------------------------- 1 | suwayomi-server.png 2 | -------------------------------------------------------------------------------- /scripts/resources/pkg/suwayomi-launcher.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Name=Suwayomi-Launcher 4 | Comment=Manga Reader 5 | Exec=/usr/bin/java -jar /usr/share/java/suwayomi-server/Suwayomi-Launcher.jar "\\$@" 6 | Icon=suwayomi-server 7 | Terminal=false 8 | Categories=Network; 9 | -------------------------------------------------------------------------------- /scripts/resources/pkg/suwayomi-launcher.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec /usr/bin/java -jar /usr/share/java/suwayomi-server/Suwayomi-Launcher.jar 4 | -------------------------------------------------------------------------------- /scripts/resources/pkg/suwayomi-server.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Name=Suwayomi-Server 4 | Comment=Manga Reader 5 | Exec=/usr/bin/java -jar /usr/share/java/suwayomi-server/bin/Suwayomi-Server.jar "\\$@" 6 | Icon=suwayomi-server 7 | Terminal=false 8 | Categories=Network; 9 | -------------------------------------------------------------------------------- /scripts/resources/pkg/suwayomi-server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec /usr/bin/java -jar /usr/share/java/suwayomi-server/bin/Suwayomi-Server.jar 4 | -------------------------------------------------------------------------------- /scripts/resources/pkg/systemd/suwayomi-server.conf: -------------------------------------------------------------------------------- 1 | TACHIDESK_ROOT_DIR="/var/lib/suwayomi" 2 | 3 | # Extra arguments passed to the java command 4 | # The default value disables the system tray icon, and launching a browser on service start. 5 | JAVA_ARGS=-Dsuwayomi.tachidesk.config.server.initialOpenInBrowserEnabled=false -Dsuwayomi.tachidesk.config.server.systemTrayEnabled=false 6 | -------------------------------------------------------------------------------- /scripts/resources/pkg/systemd/suwayomi-server.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=A free and open source manga reader server that runs extensions built for Tachiyomi. 3 | Wants=network-online.target 4 | After=network-online.target 5 | 6 | [Service] 7 | Type=simple 8 | User=suwayomi-server 9 | Group=suwayomi-server 10 | SyslogIdentifier=suwayomi-server 11 | 12 | EnvironmentFile=/etc/suwayomi/server.conf 13 | ExecStart=/usr/bin/java $JAVA_ARGS -Dsuwayomi.tachidesk.config.server.rootDir="${TACHIDESK_ROOT_DIR}" -jar /usr/share/java/suwayomi-server/bin/Suwayomi-Server.jar 14 | Restart=on-failure 15 | 16 | ProtectSystem=full 17 | ProtectHome=true 18 | PrivateTmp=yes 19 | PrivateDevices=yes 20 | ProtectClock=yes 21 | ProtectKernelTunables=yes 22 | ProtectKernelModules=yes 23 | ProtectKernelLogs=yes 24 | ProtectControlGroups=yes 25 | RestrictSUIDSGID=yes 26 | RestrictRealtime=yes 27 | RestrictNamespaces=yes 28 | NoNewPrivileges=yes 29 | 30 | [Install] 31 | WantedBy=multi-user.target 32 | -------------------------------------------------------------------------------- /scripts/resources/pkg/systemd/suwayomi-server.sysusers: -------------------------------------------------------------------------------- 1 | #Type Name ID GECOS Home directory Shell 2 | u suwayomi-server - "Suwayomi Manga Server" /var/lib/suwayomi 3 | -------------------------------------------------------------------------------- /scripts/resources/pkg/systemd/suwayomi-server.tmpfiles: -------------------------------------------------------------------------------- 1 | #Type Path Mode User Group Age Argument 2 | d /var/lib/suwayomi 0755 suwayomi-server suwayomi-server 3 | -------------------------------------------------------------------------------- /scripts/resources/suwayomi-launcher.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec ./jre/bin/java -jar ./Suwayomi-Launcher.jar 4 | -------------------------------------------------------------------------------- /scripts/resources/suwayomi-server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec ./jre/bin/java -jar ./bin/Suwayomi-Server.jar 4 | -------------------------------------------------------------------------------- /server/i18n/src/commonMain/moko-resources/files/languages.json: -------------------------------------------------------------------------------- 1 | {"langs":["en","es"]} -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/App.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import android.app.Application 11 | import android.content.Context 12 | 13 | open class App : Application() { 14 | override fun onCreate() { 15 | super.onCreate() 16 | // if (BuildConfig.DEBUG) Timber.plant(Timber.DebugTree()) 17 | } 18 | 19 | override fun attachBaseContext(base: Context) { 20 | super.attachBaseContext(base) 21 | // if (BuildConfig.DEBUG) { 22 | // MultiDex.install(this) 23 | // } 24 | } 25 | 26 | // override fun onConfigurationChanged(newConfig: Configuration) { 27 | // super.onConfigurationChanged(newConfig) 28 | // } 29 | } 30 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/AppInfo.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi 2 | 3 | import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil 4 | import suwayomi.tachidesk.server.generated.BuildConfig 5 | 6 | /** 7 | * Used by extensions. 8 | * 9 | * @since extension-lib 1.3 10 | */ 11 | object AppInfo { 12 | /** 13 | * 14 | * should be something like 74 15 | * 16 | * @since extension-lib 1.3 17 | */ 18 | fun getVersionCode() = 19 | BuildConfig.VERSION 20 | .replace("v", "") 21 | .split('.') 22 | .joinToString("") 23 | .toInt() 24 | 25 | /** 26 | * should be something like "0.13.1" 27 | * 28 | * @since extension-lib 1.3 29 | */ 30 | fun getVersionName() = BuildConfig.VERSION.substring(1) 31 | 32 | /** 33 | * A list of supported image MIME types by the reader. 34 | * e.g. ["image/jpeg", "image/png", ...] 35 | * 36 | * @since extension-lib 1.5 37 | */ 38 | fun getSupportedImageMimeTypes(): List = ImageUtil.ImageType.entries.map { it.mime } 39 | } 40 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015 Javier Tomás 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/network/JavaScriptEngine.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.network 2 | 3 | import android.content.Context 4 | import app.cash.quickjs.QuickJs 5 | import kotlinx.coroutines.Dispatchers 6 | import kotlinx.coroutines.withContext 7 | 8 | /** 9 | * Util for evaluating JavaScript in sources. 10 | */ 11 | class JavaScriptEngine( 12 | @Suppress("UNUSED_PARAMETER") context: Context, 13 | ) { 14 | /** 15 | * Evaluate arbitrary JavaScript code and get the result as a primitive type 16 | * (e.g., String, Int). 17 | * 18 | * @since extensions-lib 1.4 19 | * @param script JavaScript to execute. 20 | * @return Result of JavaScript code as a primitive type. 21 | */ 22 | @Suppress("UNUSED", "UNCHECKED_CAST") 23 | suspend fun evaluate(script: String): T = 24 | withContext(Dispatchers.IO) { 25 | QuickJs.create().use { 26 | it.evaluate(script) as T 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/network/PersistentCookieJar.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.network 2 | 3 | import okhttp3.Cookie 4 | import okhttp3.CookieJar 5 | import okhttp3.HttpUrl 6 | 7 | // from TachiWeb-Server 8 | class PersistentCookieJar( 9 | private val store: PersistentCookieStore, 10 | ) : CookieJar { 11 | override fun saveFromResponse( 12 | url: HttpUrl, 13 | cookies: List, 14 | ) { 15 | store.addAll(url, cookies) 16 | } 17 | 18 | override fun loadForRequest(url: HttpUrl): List = store.get(url) 19 | } 20 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/network/ProgressListener.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.network 2 | 3 | interface ProgressListener { 4 | fun update( 5 | bytesRead: Long, 6 | contentLength: Long, 7 | done: Boolean, 8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/IgnoreGzipInterceptor.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.network.interceptor 2 | 3 | import okhttp3.Interceptor 4 | import okhttp3.Response 5 | 6 | /** 7 | * To use [okhttp3.brotli.BrotliInterceptor] as a network interceptor, 8 | * add [IgnoreGzipInterceptor] right before it. 9 | * 10 | * This nullifies the transparent gzip of [okhttp3.internal.http.BridgeInterceptor] 11 | * so gzip and Brotli are explicitly handled by the [okhttp3.brotli.BrotliInterceptor]. 12 | */ 13 | class IgnoreGzipInterceptor : Interceptor { 14 | override fun intercept(chain: Interceptor.Chain): Response { 15 | var request = chain.request() 16 | if (request.header("Accept-Encoding") == "gzip") { 17 | request = request.newBuilder().removeHeader("Accept-Encoding").build() 18 | } 19 | return chain.proceed(request) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/UncaughtExceptionInterceptor.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.network.interceptor 2 | 3 | import okhttp3.Interceptor 4 | import okhttp3.Response 5 | import java.io.IOException 6 | 7 | /** 8 | * Catches any uncaught exceptions from later in the chain and rethrows as a non-fatal 9 | * IOException to avoid catastrophic failure. 10 | * 11 | * This should be the first interceptor in the client. 12 | * 13 | * See https://square.github.io/okhttp/4.x/okhttp/okhttp3/-interceptor/ 14 | */ 15 | class UncaughtExceptionInterceptor : Interceptor { 16 | override fun intercept(chain: Interceptor.Chain): Response = 17 | try { 18 | chain.proceed(chain.request()) 19 | } catch (e: Exception) { 20 | if (e is IOException) { 21 | throw e 22 | } else { 23 | throw IOException(e) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/UserAgentInterceptor.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.network.interceptor 2 | 3 | import okhttp3.Interceptor 4 | import okhttp3.Response 5 | 6 | class UserAgentInterceptor( 7 | private val userAgentProvider: () -> String, 8 | ) : Interceptor { 9 | override fun intercept(chain: Interceptor.Chain): Response { 10 | val originalRequest = chain.request() 11 | 12 | return if (originalRequest.header("User-Agent").isNullOrEmpty()) { 13 | val newRequest = 14 | originalRequest 15 | .newBuilder() 16 | .removeHeader("User-Agent") 17 | .addHeader("User-Agent", userAgentProvider()) 18 | .build() 19 | chain.proceed(newRequest) 20 | } else { 21 | chain.proceed(originalRequest) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/source/ConfigurableSource.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.source 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import android.content.SharedPreferences 6 | import androidx.preference.PreferenceScreen 7 | import uy.kohesive.injekt.Injekt 8 | import uy.kohesive.injekt.api.get 9 | 10 | interface ConfigurableSource : Source { 11 | /** 12 | * Gets instance of [SharedPreferences] scoped to the specific source. 13 | * 14 | * @since extensions-lib 1.5 15 | */ 16 | fun getSourcePreferences(): SharedPreferences = Injekt.get().getSharedPreferences(preferenceKey(), Context.MODE_PRIVATE) 17 | 18 | fun setupPreferenceScreen(screen: PreferenceScreen) 19 | } 20 | 21 | fun ConfigurableSource.preferenceKey(): String = "source_$id" 22 | 23 | // TODO: use getSourcePreferences once all extensions are on ext-lib 1.5 24 | fun ConfigurableSource.sourcePreferences(): SharedPreferences = 25 | Injekt.get().getSharedPreferences(preferenceKey(), Context.MODE_PRIVATE) 26 | 27 | fun sourcePreferences(key: String): SharedPreferences = Injekt.get().getSharedPreferences(key, Context.MODE_PRIVATE) 28 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/source/SourceFactory.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.source 2 | 3 | /** 4 | * A factory for creating sources at runtime. 5 | */ 6 | interface SourceFactory { 7 | /** 8 | * Create a new copy of the sources 9 | * @return The created sources 10 | */ 11 | fun createSources(): List 12 | } 13 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/source/UnmeteredSource.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.source 2 | 3 | /** 4 | * A source that explicitly doesn't require traffic considerations. 5 | * 6 | * This typically applies for self-hosted sources. 7 | */ 8 | interface UnmeteredSource 9 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/source/local/filter/OrderBy.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.source.local.filter 2 | 3 | import eu.kanade.tachiyomi.source.model.Filter 4 | 5 | sealed class OrderBy( 6 | selection: Selection, 7 | ) : Filter.Sort( 8 | "Order by", 9 | arrayOf("Title", "Date"), 10 | selection, 11 | ) { 12 | class Popular : OrderBy(Selection(0, true)) 13 | 14 | class Latest : OrderBy(Selection(1, false)) 15 | } 16 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/source/local/io/Archive.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.source.local.io 2 | 3 | import java.io.File 4 | 5 | object Archive { 6 | private val SUPPORTED_ARCHIVE_TYPES = listOf("zip", "cbz", "rar", "cbr", "epub") 7 | 8 | fun isSupported(file: File): Boolean = 9 | with(file) { 10 | return extension.lowercase() in SUPPORTED_ARCHIVE_TYPES 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/source/local/io/Format.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.source.local.io 2 | 3 | import java.io.File 4 | 5 | sealed interface Format { 6 | data class Directory( 7 | val file: File, 8 | ) : Format 9 | 10 | data class Zip( 11 | val file: File, 12 | ) : Format 13 | 14 | data class Rar( 15 | val file: File, 16 | ) : Format 17 | 18 | data class Epub( 19 | val file: File, 20 | ) : Format 21 | 22 | class UnknownFormatException : Exception() 23 | 24 | companion object { 25 | fun valueOf(file: File) = 26 | with(file) { 27 | when { 28 | isDirectory -> Directory(this) 29 | extension.equals("zip", true) || extension.equals("cbz", true) -> Zip(this) 30 | extension.equals("rar", true) || extension.equals("cbr", true) -> Rar(this) 31 | extension.equals("epub", true) -> Epub(this) 32 | else -> throw UnknownFormatException() 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/source/local/io/LocalSourceFileSystem.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.source.local.io 2 | 3 | import suwayomi.tachidesk.server.ApplicationDirs 4 | import java.io.File 5 | 6 | class LocalSourceFileSystem( 7 | private val applicationDirs: ApplicationDirs, 8 | ) { 9 | fun getBaseDirectories(): Sequence = sequenceOf(File(applicationDirs.localMangaRoot)) 10 | 11 | fun getFilesInBaseDirectories(): Sequence = 12 | getBaseDirectories() 13 | // Get all the files inside all baseDir 14 | .flatMap { it.listFiles().orEmpty().toList() } 15 | 16 | fun getMangaDirectory(name: String): File? = 17 | getFilesInBaseDirectories() 18 | // Get the first mangaDir or null 19 | .firstOrNull { it.isDirectory && it.name == name } 20 | 21 | fun getFilesInMangaDirectory(name: String): Sequence = 22 | getFilesInBaseDirectories() 23 | // Filter out ones that are not related to the manga and is not a directory 24 | .filter { it.isDirectory && it.name == name } 25 | // Get all the files inside the filtered folders 26 | .flatMap { it.listFiles().orEmpty().toList() } 27 | } 28 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/source/local/loader/EpubPageLoader.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.source.local.loader 2 | 3 | import eu.kanade.tachiyomi.util.storage.EpubFile 4 | import java.io.File 5 | 6 | /** 7 | * Loader used to load a chapter from a .epub file. 8 | */ 9 | class EpubPageLoader( 10 | file: File, 11 | ) : PageLoader { 12 | private val epub = EpubFile(file) 13 | 14 | override suspend fun getPages(): List = 15 | epub 16 | .getImagesFromPages() 17 | .mapIndexed { i, path -> 18 | val streamFn = { epub.getInputStream(epub.getEntry(path)!!) } 19 | ReaderPage(i).apply { 20 | stream = streamFn 21 | } 22 | } 23 | 24 | override fun recycle() { 25 | epub.close() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/source/local/loader/PageLoader.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.source.local.loader 2 | 3 | // adapted from eu.kanade.tachiyomi.ui.reader.loader.PageLoader 4 | interface PageLoader { 5 | /** 6 | * Returns an observable containing the list of pages of a chapter. Only the first emission 7 | * will be used. 8 | */ 9 | suspend fun getPages(): List 10 | 11 | fun recycle() 12 | } 13 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/source/local/loader/ReaderPage.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.source.local.loader 2 | 3 | import eu.kanade.tachiyomi.source.model.Page 4 | import java.io.InputStream 5 | 6 | class ReaderPage( 7 | index: Int, 8 | url: String = "", 9 | imageUrl: String? = null, 10 | var stream: (() -> InputStream)? = null, 11 | ) : Page(index, url, imageUrl, null) 12 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/source/local/loader/ZipPageLoader.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.source.local.loader 2 | 3 | import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder 4 | import org.apache.commons.compress.archivers.zip.ZipFile 5 | import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil 6 | import java.io.File 7 | 8 | /** 9 | * Loader used to load a chapter from a .zip or .cbz file. 10 | */ 11 | class ZipPageLoader( 12 | file: File, 13 | ) : PageLoader { 14 | private val zip = ZipFile.builder().setFile(file).get() 15 | 16 | override suspend fun getPages(): List = 17 | zip.entries 18 | .asSequence() 19 | .filter { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } } 20 | .sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) } 21 | .mapIndexed { i, entry -> 22 | ReaderPage(i).apply { 23 | stream = { zip.getInputStream(entry) } 24 | } 25 | }.toList() 26 | 27 | override fun recycle() { 28 | zip.close() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/source/local/metadata/MangaDetails.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.source.local.metadata 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | class MangaDetails( 7 | val title: String? = null, 8 | val author: String? = null, 9 | val artist: String? = null, 10 | val description: String? = null, 11 | val genre: List? = null, 12 | val status: Int? = null, 13 | ) 14 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/source/model/FilterList.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.source.model 2 | 3 | data class FilterList( 4 | val list: List>, 5 | ) : List> by list { 6 | constructor(vararg fs: Filter<*>) : this(if (fs.isNotEmpty()) fs.asList() else emptyList()) 7 | } 8 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/source/model/MangasPage.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.source.model 2 | 3 | data class MangasPage( 4 | val mangas: List, 5 | val hasNextPage: Boolean, 6 | ) 7 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/source/model/Page.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.source.model 2 | 3 | import android.net.Uri 4 | import eu.kanade.tachiyomi.network.ProgressListener 5 | import kotlinx.coroutines.flow.MutableStateFlow 6 | import kotlinx.coroutines.flow.asStateFlow 7 | 8 | open class Page( 9 | val index: Int, 10 | val url: String = "", 11 | var imageUrl: String? = null, 12 | // Deprecated but can't be deleted due to extensions 13 | @Transient var uri: Uri? = null, 14 | ) : ProgressListener { 15 | private val _progress = MutableStateFlow(0) 16 | val progress = _progress.asStateFlow() 17 | 18 | override fun update( 19 | bytesRead: Long, 20 | contentLength: Long, 21 | done: Boolean, 22 | ) { 23 | _progress.value = 24 | if (contentLength > 0) { 25 | (100 * bytesRead / contentLength).toInt() 26 | } else { 27 | -1 28 | } 29 | } 30 | 31 | companion object { 32 | const val QUEUE = 0 33 | const val LOAD_PAGE = 1 34 | const val DOWNLOAD_IMAGE = 2 35 | const val READY = 3 36 | const val ERROR = 4 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/source/model/SChapter.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("ktlint:standard:property-naming") 2 | 3 | package eu.kanade.tachiyomi.source.model 4 | 5 | import java.io.Serializable 6 | 7 | interface SChapter : Serializable { 8 | var url: String 9 | 10 | var name: String 11 | 12 | var date_upload: Long 13 | 14 | var chapter_number: Float 15 | 16 | var scanlator: String? 17 | 18 | fun copyFrom(other: SChapter) { 19 | name = other.name 20 | url = other.url 21 | date_upload = other.date_upload 22 | chapter_number = other.chapter_number 23 | scanlator = other.scanlator 24 | } 25 | 26 | companion object { 27 | fun create(): SChapter = SChapterImpl() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/source/model/SChapterImpl.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("ktlint:standard:property-naming") 2 | 3 | package eu.kanade.tachiyomi.source.model 4 | 5 | class SChapterImpl : SChapter { 6 | override lateinit var url: String 7 | 8 | override lateinit var name: String 9 | 10 | override var date_upload: Long = 0 11 | 12 | override var chapter_number: Float = -1f 13 | 14 | override var scanlator: String? = null 15 | } 16 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/source/model/SMangaImpl.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("ktlint:standard:property-naming") 2 | 3 | package eu.kanade.tachiyomi.source.model 4 | 5 | class SMangaImpl : SManga { 6 | override lateinit var url: String 7 | 8 | override lateinit var title: String 9 | 10 | override var artist: String? = null 11 | 12 | override var author: String? = null 13 | 14 | override var description: String? = null 15 | 16 | override var genre: String? = null 17 | 18 | override var status: Int = 0 19 | 20 | override var thumbnail_url: String? = null 21 | 22 | override var update_strategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE 23 | 24 | override var initialized: Boolean = false 25 | } 26 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/source/model/UpdateStrategy.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.source.model 2 | 3 | enum class UpdateStrategy { 4 | ALWAYS_UPDATE, 5 | ONLY_FETCH_ONCE, 6 | } 7 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/source/online/ResolvableSource.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.source.online 2 | 3 | import eu.kanade.tachiyomi.source.Source 4 | import eu.kanade.tachiyomi.source.model.SManga 5 | 6 | /** 7 | * A source that may handle opening an SManga for a given URI. 8 | * 9 | * @since extensions-lib 1.5 10 | */ 11 | @Suppress("unused") 12 | interface ResolvableSource : Source { 13 | /** 14 | * Whether this source may potentially handle the given URI. 15 | * 16 | * @since extensions-lib 1.5 17 | */ 18 | fun canResolveUri(uri: String): Boolean 19 | 20 | /** 21 | * Called if canHandleUri is true. Returns the corresponding SManga, if possible. 22 | * 23 | * @since extensions-lib 1.5 24 | */ 25 | suspend fun getManga(uri: String): SManga? 26 | } 27 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/util/JsoupExtensions.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.util 2 | 3 | import okhttp3.Response 4 | import org.jsoup.Jsoup 5 | import org.jsoup.nodes.Document 6 | import org.jsoup.nodes.Element 7 | 8 | fun Element.selectText( 9 | css: String, 10 | defaultValue: String? = null, 11 | ): String? = select(css).first()?.text() ?: defaultValue 12 | 13 | fun Element.selectInt( 14 | css: String, 15 | defaultValue: Int = 0, 16 | ): Int = select(css).first()?.text()?.toInt() ?: defaultValue 17 | 18 | fun Element.attrOrText(css: String): String = if (css != "text") attr(css) else text() 19 | 20 | /** 21 | * Returns a Jsoup document for this response. 22 | * @param html the body of the response. Use only if the body was read before calling this method. 23 | */ 24 | fun Response.asJsoup(html: String? = null): Document = Jsoup.parse(html ?: body.string(), request.url.toString()) 25 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/util/PkceUtil.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.util 2 | 3 | import android.util.Base64 4 | import java.security.SecureRandom 5 | 6 | object PkceUtil { 7 | private const val PKCE_BASE64_ENCODE_SETTINGS = Base64.NO_WRAP or Base64.NO_PADDING or Base64.URL_SAFE 8 | 9 | fun generateCodeVerifier(): String { 10 | val codeVerifier = ByteArray(50) 11 | SecureRandom().nextBytes(codeVerifier) 12 | return Base64.encodeToString(codeVerifier, PKCE_BASE64_ENCODE_SETTINGS) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /server/src/main/kotlin/eu/kanade/tachiyomi/util/chapter/ChapterSanitizer.kt: -------------------------------------------------------------------------------- 1 | package eu.kanade.tachiyomi.util.chapter 2 | 3 | object ChapterSanitizer { 4 | fun String.sanitize(title: String): String = 5 | trim() 6 | .removePrefix(title) 7 | .trim(*CHAPTER_TRIM_CHARS) 8 | 9 | private val CHAPTER_TRIM_CHARS = 10 | arrayOf( 11 | // Whitespace 12 | ' ', 13 | '\u0009', 14 | '\u000A', 15 | '\u000B', 16 | '\u000C', 17 | '\u000D', 18 | '\u0020', 19 | '\u0085', 20 | '\u00A0', 21 | '\u1680', 22 | '\u2000', 23 | '\u2001', 24 | '\u2002', 25 | '\u2003', 26 | '\u2004', 27 | '\u2005', 28 | '\u2006', 29 | '\u2007', 30 | '\u2008', 31 | '\u2009', 32 | '\u200A', 33 | '\u2028', 34 | '\u2029', 35 | '\u202F', 36 | '\u205F', 37 | '\u3000', 38 | // Separators 39 | '-', 40 | '_', 41 | ',', 42 | ':', 43 | ).toCharArray() 44 | } 45 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/Main.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import suwayomi.tachidesk.server.JavalinSetup.javalinSetup 11 | import suwayomi.tachidesk.server.applicationSetup 12 | 13 | fun main() { 14 | applicationSetup() 15 | javalinSetup() 16 | } 17 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/global/GlobalAPI.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.global 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import io.javalin.apibuilder.ApiBuilder.get 11 | import io.javalin.apibuilder.ApiBuilder.patch 12 | import io.javalin.apibuilder.ApiBuilder.path 13 | import suwayomi.tachidesk.global.controller.GlobalMetaController 14 | import suwayomi.tachidesk.global.controller.SettingsController 15 | 16 | object GlobalAPI { 17 | fun defineEndpoints() { 18 | path("meta") { 19 | get("", GlobalMetaController.getMeta) 20 | patch("", GlobalMetaController.modifyMeta) 21 | } 22 | path("settings") { 23 | get("about", SettingsController.about) 24 | get("check-update", SettingsController.checkUpdate) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/global/impl/About.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.global.impl 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import suwayomi.tachidesk.server.generated.BuildConfig 11 | 12 | data class AboutDataClass( 13 | val name: String, 14 | val version: String, 15 | @Deprecated("The version includes the revision as the patch number") 16 | val revision: String, 17 | val buildType: String, 18 | val buildTime: Long, 19 | val github: String, 20 | val discord: String, 21 | ) 22 | 23 | object About { 24 | fun getAbout(): AboutDataClass = 25 | AboutDataClass( 26 | BuildConfig.NAME, 27 | BuildConfig.VERSION, 28 | BuildConfig.REVISION, 29 | BuildConfig.BUILD_TYPE, 30 | BuildConfig.BUILD_TIME, 31 | BuildConfig.GITHUB, 32 | BuildConfig.DISCORD, 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/global/model/table/GlobalMetaTable.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.global.model.table 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import org.jetbrains.exposed.dao.id.IntIdTable 11 | 12 | /** 13 | * Metadata storage for clients, server/global level. 14 | */ 15 | object GlobalMetaTable : IntIdTable() { 16 | val key = varchar("key", 256) 17 | val value = varchar("value", 4096) 18 | } 19 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/graphql/AsDataFetcherResult.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.graphql 2 | 3 | import com.expediagroup.graphql.server.extensions.toGraphQLError 4 | import graphql.execution.DataFetcherResult 5 | import io.github.oshai.kotlinlogging.KotlinLogging 6 | 7 | val logger = KotlinLogging.logger { } 8 | 9 | inline fun asDataFetcherResult(block: () -> T): DataFetcherResult { 10 | val result = 11 | runCatching { 12 | block() 13 | } 14 | 15 | if (result.isFailure) { 16 | logger.error(result.exceptionOrNull()) { "asDataFetcherResult: failed due to" } 17 | return DataFetcherResult 18 | .newResult() 19 | .error(result.exceptionOrNull()?.toGraphQLError()) 20 | .build() 21 | } 22 | 23 | return DataFetcherResult 24 | .newResult() 25 | .data(result.getOrNull()) 26 | .build() 27 | } 28 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/graphql/GraphQL.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Contributors to the Suwayomi project 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 7 | 8 | package suwayomi.tachidesk.graphql 9 | 10 | import io.javalin.apibuilder.ApiBuilder.get 11 | import io.javalin.apibuilder.ApiBuilder.path 12 | import io.javalin.apibuilder.ApiBuilder.post 13 | import io.javalin.apibuilder.ApiBuilder.ws 14 | import suwayomi.tachidesk.graphql.controller.GraphQLController 15 | 16 | object GraphQL { 17 | fun defineEndpoints() { 18 | post("graphql", GraphQLController::execute) 19 | ws("graphql", GraphQLController::webSocket) 20 | 21 | // graphql playground 22 | get("graphql", GraphQLController::playground) 23 | 24 | path("graphql/files") { 25 | get("backup/{file}", GraphQLController::retrieveFile) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/graphql/cache/CustomCacheMap.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.graphql.cache 2 | 3 | import org.dataloader.CacheMap 4 | import java.util.concurrent.CompletableFuture 5 | 6 | class CustomCacheMap : CacheMap { 7 | private val cache: MutableMap> 8 | 9 | init { 10 | cache = HashMap() 11 | } 12 | 13 | override fun containsKey(key: K): Boolean = cache.containsKey(key) 14 | 15 | override fun get(key: K): CompletableFuture = cache[key]!! 16 | 17 | fun getKeys(): Collection = cache.keys.toSet() 18 | 19 | override fun getAll(): Collection> = cache.values 20 | 21 | override fun set( 22 | key: K, 23 | value: CompletableFuture, 24 | ): CacheMap { 25 | cache[key] = value 26 | return this 27 | } 28 | 29 | override fun delete(key: K): CacheMap { 30 | cache.remove(key) 31 | return this 32 | } 33 | 34 | override fun clear(): CacheMap { 35 | cache.clear() 36 | return this 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/DownloadQuery.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.graphql.queries 2 | 3 | import suwayomi.tachidesk.graphql.types.DownloadStatus 4 | import suwayomi.tachidesk.manga.impl.download.DownloadManager 5 | import suwayomi.tachidesk.server.JavalinSetup.future 6 | import java.util.concurrent.CompletableFuture 7 | 8 | class DownloadQuery { 9 | fun downloadStatus(): CompletableFuture = 10 | future { 11 | DownloadStatus(DownloadManager.getStatus()) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/SettingsQuery.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.graphql.queries 2 | 3 | import suwayomi.tachidesk.graphql.types.SettingsType 4 | 5 | class SettingsQuery { 6 | fun settings(): SettingsType = SettingsType() 7 | } 8 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/UpdateQuery.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.graphql.queries 2 | 3 | import com.expediagroup.graphql.generator.annotations.GraphQLDeprecated 4 | import kotlinx.coroutines.flow.first 5 | import suwayomi.tachidesk.graphql.types.LibraryUpdateStatus 6 | import suwayomi.tachidesk.graphql.types.UpdateStatus 7 | import suwayomi.tachidesk.manga.impl.update.IUpdater 8 | import suwayomi.tachidesk.server.JavalinSetup.future 9 | import uy.kohesive.injekt.injectLazy 10 | import java.util.concurrent.CompletableFuture 11 | 12 | class UpdateQuery { 13 | private val updater: IUpdater by injectLazy() 14 | 15 | @GraphQLDeprecated("Replaced with libraryUpdateStatus", ReplaceWith("libraryUpdateStatus")) 16 | fun updateStatus(): CompletableFuture = future { UpdateStatus(updater.status.first()) } 17 | 18 | fun libraryUpdateStatus(): CompletableFuture = future { LibraryUpdateStatus(updater.getStatus()) } 19 | 20 | data class LastUpdateTimestampPayload( 21 | val timestamp: Long, 22 | ) 23 | 24 | fun lastUpdateTimestamp(): LastUpdateTimestampPayload = LastUpdateTimestampPayload(updater.getLastUpdateTimestamp()) 25 | } 26 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/QueryResults.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.graphql.server.primitives 2 | 3 | import org.jetbrains.exposed.sql.ResultRow 4 | 5 | data class QueryResults( 6 | val total: Long, 7 | val firstKey: T, 8 | val lastKey: T, 9 | val results: List, 10 | ) 11 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/graphql/server/subscriptions/FlowSubscriptionSource.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Contributors to the Suwayomi project 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 7 | 8 | package suwayomi.tachidesk.graphql.server.subscriptions 9 | 10 | import kotlinx.coroutines.flow.MutableSharedFlow 11 | import kotlinx.coroutines.flow.asSharedFlow 12 | 13 | class FlowSubscriptionSource { 14 | private val mutableSharedFlow = MutableSharedFlow() 15 | val emitter = mutableSharedFlow.asSharedFlow() 16 | 17 | fun publish(value: T) { 18 | mutableSharedFlow.tryEmit(value) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/graphql/subscriptions/InfoSubscription.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.graphql.subscriptions 2 | 3 | import kotlinx.coroutines.flow.Flow 4 | import suwayomi.tachidesk.graphql.types.WebUIUpdateStatus 5 | import suwayomi.tachidesk.server.util.WebInterfaceManager 6 | 7 | class InfoSubscription { 8 | fun webUIUpdateStatusChange(): Flow = WebInterfaceManager.status 9 | } 10 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/ThumbnailDownloadHelper.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl 2 | 3 | import suwayomi.tachidesk.manga.impl.download.fileProvider.impl.ThumbnailFileProvider 4 | import java.io.InputStream 5 | 6 | object ThumbnailDownloadHelper { 7 | fun getImage(mangaId: Int): Pair = provider(mangaId).getImage().execute() 8 | 9 | fun delete(mangaId: Int): Boolean = provider(mangaId).delete() 10 | 11 | suspend fun download(mangaId: Int): Boolean = provider(mangaId).download().execute() 12 | 13 | // return the appropriate provider based on how the download was saved. For the logic is simple but will evolve when new types of downloads are available 14 | private fun provider(mangaId: Int): ThumbnailFileProvider = ThumbnailFileProvider(mangaId) 15 | } 16 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/BackupFlags.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.backup 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | data class BackupFlags( 11 | val includeManga: Boolean, 12 | val includeCategories: Boolean, 13 | val includeChapters: Boolean, 14 | val includeTracking: Boolean, 15 | val includeHistory: Boolean, 16 | ) 17 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/Chapter.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("ktlint:standard:property-naming") 2 | 3 | package suwayomi.tachidesk.manga.impl.backup.models 4 | 5 | import eu.kanade.tachiyomi.source.model.SChapter 6 | import java.io.Serializable 7 | 8 | interface Chapter : 9 | SChapter, 10 | Serializable { 11 | var id: Long? 12 | 13 | var manga_id: Long? 14 | 15 | var read: Boolean 16 | 17 | var bookmark: Boolean 18 | 19 | var last_page_read: Int 20 | 21 | var date_fetch: Long 22 | 23 | var source_order: Int 24 | 25 | val isRecognizedNumber: Boolean 26 | get() = chapter_number >= 0f 27 | } 28 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/ChapterImpl.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("ktlint:standard:property-naming") 2 | 3 | package suwayomi.tachidesk.manga.impl.backup.models 4 | 5 | class ChapterImpl : Chapter { 6 | override var id: Long? = null 7 | 8 | override var manga_id: Long? = null 9 | 10 | override lateinit var url: String 11 | 12 | override lateinit var name: String 13 | 14 | override var scanlator: String? = null 15 | 16 | override var read: Boolean = false 17 | 18 | override var bookmark: Boolean = false 19 | 20 | override var last_page_read: Int = 0 21 | 22 | override var date_fetch: Long = 0 23 | 24 | override var date_upload: Long = 0 25 | 26 | override var chapter_number: Float = 0f 27 | 28 | override var source_order: Int = 0 29 | 30 | override fun equals(other: Any?): Boolean { 31 | if (this === other) return true 32 | if (other == null || javaClass != other.javaClass) return false 33 | 34 | val chapter = other as Chapter 35 | if (url != chapter.url) return false 36 | return id == chapter.id 37 | } 38 | 39 | override fun hashCode(): Int = url.hashCode() + id.hashCode() 40 | } 41 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/models/Track.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("ktlint:standard:property-naming") 2 | 3 | package suwayomi.tachidesk.manga.impl.backup.models 4 | 5 | import java.io.Serializable 6 | 7 | interface Track : Serializable { 8 | var id: Long? 9 | 10 | var manga_id: Long 11 | 12 | var sync_id: Int 13 | 14 | var media_id: Long 15 | 16 | var library_id: Long? 17 | 18 | var title: String 19 | 20 | var last_chapter_read: Int 21 | 22 | var total_chapters: Int 23 | 24 | var score: Float 25 | 26 | var status: Int 27 | 28 | var started_reading_date: Long 29 | 30 | var finished_reading_date: Long 31 | 32 | var tracking_url: String 33 | } 34 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/ProtoBackupBase.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.backup.proto 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import kotlinx.serialization.protobuf.ProtoBuf 11 | 12 | open class ProtoBackupBase { 13 | var sourceMapping: Map = emptyMap() 14 | 15 | val parser = ProtoBuf 16 | } 17 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/models/BackupCategory.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.backup.proto.models 2 | 3 | import kotlinx.serialization.Serializable 4 | import kotlinx.serialization.protobuf.ProtoNumber 5 | 6 | @Serializable 7 | class BackupCategory( 8 | @ProtoNumber(1) var name: String, 9 | @ProtoNumber(2) var order: Int = 0, 10 | // @ProtoNumber(3) val updateInterval: Int = 0, 1.x value not used in 0.x 11 | // Bump by 100 to specify this is a 0.x value 12 | @ProtoNumber(100) var flags: Int = 0, 13 | ) 14 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/models/BackupHistory.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.backup.proto.models 2 | 3 | import kotlinx.serialization.Serializable 4 | import kotlinx.serialization.protobuf.ProtoNumber 5 | 6 | @Serializable 7 | data class BackupHistory( 8 | @ProtoNumber(1) var url: String, 9 | @ProtoNumber(2) var lastRead: Long, 10 | ) 11 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/backup/proto/models/BackupSource.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.backup.proto.models 2 | 3 | import eu.kanade.tachiyomi.source.Source 4 | import kotlinx.serialization.Serializable 5 | import kotlinx.serialization.protobuf.ProtoNumber 6 | 7 | @Serializable 8 | data class BackupSource( 9 | @ProtoNumber(1) var name: String = "", 10 | @ProtoNumber(2) var sourceId: Long, 11 | ) { 12 | companion object { 13 | fun copyFrom(source: Source): BackupSource = 14 | BackupSource( 15 | name = source.name, 16 | sourceId = source.id, 17 | ) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/DownloadedFilesProvider.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.download.fileProvider 2 | 3 | interface DownloadedFilesProvider : 4 | FileDownloader, 5 | FileRetriever { 6 | fun delete(): Boolean 7 | } 8 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/FileDownloader.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.download.fileProvider 2 | 3 | fun interface FileDownload { 4 | suspend fun executeDownload(vararg args: Any): Boolean 5 | } 6 | 7 | fun interface FileDownload0Args : FileDownload { 8 | suspend fun execute(): Boolean 9 | 10 | override suspend fun executeDownload(vararg args: Any): Boolean = execute() 11 | } 12 | 13 | @Suppress("UNCHECKED_CAST") 14 | fun interface FileDownload3Args : FileDownload { 15 | suspend fun execute( 16 | a: A, 17 | b: B, 18 | c: C, 19 | ): Boolean 20 | 21 | override suspend fun executeDownload(vararg args: Any): Boolean = execute(args[0] as A, args[1] as B, args[2] as C) 22 | } 23 | 24 | fun interface FileDownloader { 25 | fun download(): FileDownload 26 | } 27 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/FileRetriever.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.download.fileProvider 2 | 3 | import java.io.InputStream 4 | 5 | fun interface RetrieveFile { 6 | fun executeGetImage(vararg args: Any): Pair 7 | } 8 | 9 | fun interface RetrieveFile0Args : RetrieveFile { 10 | fun execute(): Pair 11 | 12 | override fun executeGetImage(vararg args: Any): Pair = execute() 13 | } 14 | 15 | @Suppress("UNCHECKED_CAST") 16 | fun interface RetrieveFile1Args : RetrieveFile { 17 | fun execute(a: A): Pair 18 | 19 | override fun executeGetImage(vararg args: Any): Pair = execute(args[0] as A) 20 | } 21 | 22 | fun interface FileRetriever { 23 | fun getImage(): RetrieveFile 24 | } 25 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/model/DownloadChapter.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.download.model 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import suwayomi.tachidesk.manga.impl.download.model.DownloadState.Queued 11 | import suwayomi.tachidesk.manga.model.dataclass.ChapterDataClass 12 | import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass 13 | 14 | class DownloadChapter( 15 | val chapterIndex: Int, 16 | val mangaId: Int, 17 | var chapter: ChapterDataClass, 18 | var manga: MangaDataClass, 19 | var position: Int, 20 | var state: DownloadState = Queued, 21 | var progress: Float = 0f, 22 | var tries: Int = 0, 23 | ) { 24 | override fun toString(): String = 25 | "${manga.title} ($mangaId) - ${chapter.name} (${chapter.id}) | state= $state, tries= $tries, progress= $progress" 26 | } 27 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/model/DownloadState.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.download.model 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | enum class DownloadState( 11 | val state: Int, 12 | ) { 13 | Queued(0), 14 | Downloading(1), 15 | Finished(2), 16 | Error(3), 17 | } 18 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/model/DownloadStatus.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.download.model 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | enum class Status { 11 | Stopped, 12 | Started, 13 | } 14 | 15 | data class DownloadStatus( 16 | val status: Status, 17 | val queue: List, 18 | ) 19 | 20 | data class DownloadUpdates( 21 | val status: Status, 22 | val updates: List, 23 | val initial: List?, 24 | ) 25 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/model/DownloadUpdate.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.download.model 2 | 3 | enum class DownloadUpdateType { 4 | QUEUED, 5 | DEQUEUED, 6 | PAUSED, 7 | STOPPED, 8 | PROGRESS, 9 | FINISHED, 10 | ERROR, 11 | POSITION, 12 | } 13 | 14 | data class DownloadUpdate( 15 | val type: DownloadUpdateType, 16 | val downloadChapter: DownloadChapter, 17 | ) 18 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/extension/github/OnlineExtension.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.extension.github 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | data class OnlineExtensionSource( 11 | val name: String, 12 | val lang: String, 13 | val id: Long, 14 | val baseUrl: String, 15 | ) 16 | 17 | data class OnlineExtension( 18 | val repo: String, 19 | val name: String, 20 | val pkgName: String, 21 | val apkName: String, 22 | val lang: String, 23 | val versionCode: Int, 24 | val versionName: String, 25 | val isNsfw: Boolean, 26 | val hasReadme: Boolean, 27 | val hasChangelog: Boolean, 28 | val sources: List, 29 | val iconUrl: String, 30 | ) 31 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/DeletableTrackService.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.track.tracker 2 | 3 | import suwayomi.tachidesk.manga.impl.track.tracker.model.Track 4 | 5 | /** 6 | * For track services api that support deleting a manga entry for a user's list 7 | */ 8 | interface DeletableTrackService { 9 | suspend fun delete(track: Track) 10 | } 11 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/kitsu/KitsuDateHelper.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.track.tracker.kitsu 2 | 3 | import java.text.SimpleDateFormat 4 | import java.util.Date 5 | import java.util.Locale 6 | 7 | object KitsuDateHelper { 8 | private const val PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" 9 | private val formatter = SimpleDateFormat(PATTERN, Locale.ENGLISH) 10 | 11 | fun convert(dateValue: Long): String? { 12 | if (dateValue == 0L) return null 13 | 14 | return formatter.format(Date(dateValue)) 15 | } 16 | 17 | fun parse(dateString: String?): Long { 18 | if (dateString == null) return 0L 19 | 20 | val dateValue = formatter.parse(dateString) 21 | 22 | return dateValue?.time ?: return 0 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/mangaupdates/MangaUpdatesInterceptor.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates 2 | 3 | import okhttp3.Interceptor 4 | import okhttp3.Response 5 | import suwayomi.tachidesk.server.generated.BuildConfig 6 | import java.io.IOException 7 | 8 | class MangaUpdatesInterceptor( 9 | mangaUpdates: MangaUpdates, 10 | ) : Interceptor { 11 | private var token: String? = mangaUpdates.restoreSession() 12 | 13 | override fun intercept(chain: Interceptor.Chain): Response { 14 | val originalRequest = chain.request() 15 | 16 | val token = token ?: throw IOException("Not authenticated with MangaUpdates") 17 | 18 | // Add the authorization header to the original request. 19 | val authRequest = 20 | originalRequest 21 | .newBuilder() 22 | .addHeader("Authorization", "Bearer $token") 23 | .header("User-Agent", "Suwayomi ${BuildConfig.VERSION}") 24 | .build() 25 | 26 | return chain.proceed(authRequest) 27 | } 28 | 29 | fun newAuth(token: String?) { 30 | this.token = token 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/mangaupdates/dto/Context.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | 6 | @Serializable 7 | data class Context( 8 | @SerialName("session_token") 9 | val sessionToken: String, 10 | val uid: Long, 11 | ) 12 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/mangaupdates/dto/Image.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class Image( 7 | val url: Url? = null, 8 | val height: Int? = null, 9 | val width: Int? = null, 10 | ) 11 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/mangaupdates/dto/ListItem.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.MangaUpdates.Companion.READING_LIST 6 | import suwayomi.tachidesk.manga.impl.track.tracker.model.Track 7 | 8 | @Serializable 9 | data class ListItem( 10 | val series: Series? = null, 11 | @SerialName("list_id") 12 | val listId: Int? = null, 13 | val status: Status? = null, 14 | val priority: Int? = null, 15 | ) 16 | 17 | fun ListItem.copyTo(track: Track): Track = 18 | track.apply { 19 | this.status = listId ?: READING_LIST 20 | this.last_chapter_read = this@copyTo.status?.chapter?.toFloat() ?: 0f 21 | } 22 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/mangaupdates/dto/Rating.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto 2 | 3 | import kotlinx.serialization.Serializable 4 | import suwayomi.tachidesk.manga.impl.track.tracker.model.Track 5 | 6 | @Serializable 7 | data class Rating( 8 | val rating: Float? = null, 9 | ) 10 | 11 | fun Rating.copyTo(track: Track): Track = 12 | track.apply { 13 | this.score = rating ?: 0f 14 | } 15 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/mangaupdates/dto/Series.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class Series( 7 | val id: Long? = null, 8 | val title: String? = null, 9 | ) 10 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/mangaupdates/dto/Status.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class Status( 7 | val volume: Int? = null, 8 | val chapter: Int? = null, 9 | ) 10 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/mangaupdates/dto/Url.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class Url( 7 | val original: String? = null, 8 | val thumb: String? = null, 9 | ) 10 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/model/Track.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("ktlint:standard:property-naming") 2 | 3 | package suwayomi.tachidesk.manga.impl.track.tracker.model 4 | 5 | import java.io.Serializable 6 | 7 | interface Track : Serializable { 8 | var id: Int? 9 | 10 | var manga_id: Int 11 | 12 | var sync_id: Int 13 | 14 | var media_id: Long 15 | 16 | var library_id: Long? 17 | 18 | var title: String 19 | 20 | var last_chapter_read: Float 21 | 22 | var total_chapters: Int 23 | 24 | var score: Float 25 | 26 | var status: Int 27 | 28 | var started_reading_date: Long 29 | 30 | var finished_reading_date: Long 31 | 32 | var tracking_url: String 33 | 34 | fun copyPersonalFrom(other: Track) { 35 | last_chapter_read = other.last_chapter_read 36 | score = other.score 37 | status = other.status 38 | started_reading_date = other.started_reading_date 39 | finished_reading_date = other.finished_reading_date 40 | } 41 | 42 | companion object { 43 | fun create(serviceId: Int): Track = 44 | TrackImpl().apply { 45 | sync_id = serviceId 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/model/TrackImpl.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("ktlint:standard:property-naming") 2 | 3 | package suwayomi.tachidesk.manga.impl.track.tracker.model 4 | 5 | class TrackImpl : Track { 6 | override var id: Int? = null 7 | 8 | override var manga_id: Int = 0 9 | 10 | override var sync_id: Int = 0 11 | 12 | override var media_id: Long = 0 13 | 14 | override var library_id: Long? = null 15 | 16 | override lateinit var title: String 17 | 18 | override var last_chapter_read: Float = 0F 19 | 20 | override var total_chapters: Int = 0 21 | 22 | override var score: Float = 0f 23 | 24 | override var status: Int = 0 25 | 26 | override var started_reading_date: Long = 0 27 | 28 | override var finished_reading_date: Long = 0 29 | 30 | override var tracking_url: String = "" 31 | } 32 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/update/IUpdater.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.update 2 | 3 | import kotlinx.coroutines.flow.Flow 4 | import kotlinx.coroutines.flow.StateFlow 5 | import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass 6 | import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass 7 | 8 | interface IUpdater { 9 | fun getLastUpdateTimestamp(): Long 10 | 11 | fun addCategoriesToUpdateQueue( 12 | categories: List, 13 | clear: Boolean?, 14 | forceAll: Boolean, 15 | ) 16 | 17 | fun addMangasToQueue(mangas: List) 18 | 19 | @Deprecated("Replaced with updates", replaceWith = ReplaceWith("updates")) 20 | val status: Flow 21 | 22 | val updates: Flow 23 | 24 | val statusDeprecated: StateFlow 25 | 26 | fun reset() 27 | 28 | fun getStatus(): UpdateUpdates 29 | } 30 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/update/UpdateJob.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.update 2 | 3 | import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass 4 | import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass 5 | 6 | enum class JobStatus { 7 | PENDING, 8 | RUNNING, 9 | COMPLETE, 10 | FAILED, 11 | SKIPPED, 12 | } 13 | 14 | data class UpdateJob( 15 | val manga: MangaDataClass, 16 | val status: JobStatus = JobStatus.PENDING, 17 | ) 18 | 19 | enum class CategoryUpdateStatus { 20 | UPDATING, 21 | SKIPPED, 22 | } 23 | 24 | data class CategoryUpdateJob( 25 | val category: CategoryDataClass, 26 | val status: CategoryUpdateStatus, 27 | ) 28 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/update/Websocket.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.update 2 | 3 | import io.javalin.websocket.WsContext 4 | import io.javalin.websocket.WsMessageContext 5 | import java.util.concurrent.ConcurrentHashMap 6 | 7 | abstract class Websocket { 8 | protected val clients = ConcurrentHashMap() 9 | 10 | open fun addClient(ctx: WsContext) { 11 | clients[ctx.sessionId()] = ctx 12 | notifyClient(ctx, null) 13 | } 14 | 15 | open fun removeClient(ctx: WsContext) { 16 | clients.remove(ctx.sessionId()) 17 | } 18 | 19 | open fun notifyAllClients(value: T) { 20 | clients.values.forEach { notifyClient(it, value) } 21 | } 22 | 23 | abstract fun notifyClient( 24 | ctx: WsContext, 25 | value: T?, 26 | ) 27 | 28 | abstract fun handleRequest(ctx: WsMessageContext) 29 | } 30 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/lang/ExposedExtensions.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.util.lang 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import org.jetbrains.exposed.sql.Query 11 | 12 | fun Query.isEmpty() = this.empty() 13 | 14 | fun Query.isNotEmpty() = !this.isEmpty() 15 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/lang/FileExt.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.util.lang 2 | 3 | import java.io.File 4 | 5 | /* 6 | * Copyright (C) Contributors to the Suwayomi project 7 | * 8 | * This Source Code Form is subject to the terms of the Mozilla Public 9 | * License, v. 2.0. If a copy of the MPL was not distributed with this 10 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 11 | 12 | fun File.renameTo(newPath: String) = renameTo(File(newPath)) 13 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/lang/List.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.util.lang 2 | 3 | fun List.trimAll() = map { it.trim() } 4 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/storage/FileDeletionHelper.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.util.storage 2 | 3 | import java.io.File 4 | 5 | object FileDeletionHelper { 6 | /** 7 | * Recursively deletes all parent folders for the given deleted file until the parent folder is not empty, or it's the root folder 8 | */ 9 | fun cleanupParentFoldersFor( 10 | file: File, 11 | rootPath: String, 12 | ) { 13 | val folder = file.parentFile 14 | if (!folder.isDirectory) { 15 | return 16 | } 17 | 18 | if (folder.absolutePath == rootPath) { 19 | return 20 | } 21 | 22 | if (folder.listFiles()?.isEmpty() != true) { 23 | return 24 | } 25 | 26 | folder.delete() 27 | cleanupParentFoldersFor(folder, rootPath) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/storage/Io.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl.util.storage 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import java.util.zip.ZipEntry 11 | import java.util.zip.ZipInputStream 12 | 13 | fun ZipEntry.use( 14 | stream: ZipInputStream, 15 | block: (ZipEntry) -> Unit, 16 | ) { 17 | var exception: Throwable? = null 18 | try { 19 | return block(this) 20 | } catch (e: Throwable) { 21 | exception = e 22 | throw e 23 | } finally { 24 | if (exception == null) { 25 | stream.closeEntry() 26 | } else { 27 | try { 28 | stream.closeEntry() 29 | } catch (closeException: Throwable) { 30 | exception.addSuppressed(closeException) 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/CategoryDataClass.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.model.dataclass 2 | 3 | import com.fasterxml.jackson.annotation.JsonValue 4 | 5 | /* 6 | * Copyright (C) Contributors to the Suwayomi project 7 | * 8 | * This Source Code Form is subject to the terms of the Mozilla Public 9 | * License, v. 2.0. If a copy of the MPL was not distributed with this 10 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 11 | 12 | enum class IncludeOrExclude( 13 | @JsonValue val value: Int, 14 | ) { 15 | EXCLUDE(0), 16 | INCLUDE(1), 17 | UNSET(-1), 18 | ; 19 | 20 | companion object { 21 | fun fromValue(value: Int) = IncludeOrExclude.values().find { it.value == value } ?: UNSET 22 | } 23 | } 24 | 25 | data class CategoryDataClass( 26 | val id: Int, 27 | val order: Int, 28 | val name: String, 29 | val default: Boolean, 30 | val size: Int, 31 | val includeInUpdate: IncludeOrExclude, 32 | val includeInDownload: IncludeOrExclude, 33 | val meta: Map = emptyMap(), 34 | ) 35 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/ExtensionDataClass.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.model.dataclass 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | data class ExtensionDataClass( 11 | val repo: String?, 12 | val apkName: String, 13 | val iconUrl: String, 14 | val name: String, 15 | val pkgName: String, 16 | val versionName: String, 17 | val versionCode: Int, 18 | val lang: String, 19 | val isNsfw: Boolean, 20 | val installed: Boolean, 21 | val hasUpdate: Boolean, 22 | val obsolete: Boolean, 23 | ) 24 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/MangaChapterDataClass.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.model.dataclass 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | data class MangaChapterDataClass( 11 | val manga: MangaDataClass, 12 | val chapter: ChapterDataClass, 13 | ) 14 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/MangaTrackerDataClass.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.model.dataclass 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | data class MangaTrackerDataClass( 11 | val id: Int, 12 | val name: String, 13 | val icon: String, 14 | val statusList: List, 15 | val statusTextMap: Map, 16 | val scoreList: List, 17 | val record: TrackRecordDataClass?, 18 | ) 19 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/PageDataClass.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.model.dataclass 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | data class PageDataClass( 11 | val index: Int, 12 | var imageUrl: String, 13 | ) 14 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/PaginatedList.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.model.dataclass 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import kotlin.math.min 11 | 12 | open class PaginatedList( 13 | val page: List, 14 | val hasNextPage: Boolean, 15 | ) 16 | 17 | const val PAGINATION_FACTOR = 50 18 | 19 | fun paginatedFrom( 20 | pageNum: Int, 21 | paginationFactor: Int = PAGINATION_FACTOR, 22 | lister: () -> List, 23 | ): PaginatedList { 24 | val list = lister() 25 | val lastIndex = list.size - 1 26 | 27 | val lowerIndex = pageNum * paginationFactor 28 | val higherIndex = (pageNum + 1) * paginationFactor - 1 29 | 30 | if (lowerIndex > lastIndex) { 31 | return PaginatedList(emptyList(), false) 32 | } 33 | 34 | val sliced = list.slice(lowerIndex..min(lastIndex, higherIndex)) 35 | 36 | return PaginatedList( 37 | sliced, 38 | higherIndex < lastIndex, 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/SourceDataClass.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.model.dataclass 2 | 3 | import eu.kanade.tachiyomi.source.ConfigurableSource 4 | 5 | /* 6 | * Copyright (C) Contributors to the Suwayomi project 7 | * 8 | * This Source Code Form is subject to the terms of the Mozilla Public 9 | * License, v. 2.0. If a copy of the MPL was not distributed with this 10 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 11 | 12 | data class SourceDataClass( 13 | val id: String, 14 | val name: String, 15 | val lang: String, 16 | val iconUrl: String, 17 | /** The Source provides a latest listing */ 18 | val supportsLatest: Boolean, 19 | /** The Source implements [ConfigurableSource] */ 20 | val isConfigurable: Boolean, 21 | /** The Source class has a @Nsfw annotation */ 22 | val isNsfw: Boolean, 23 | /** A nicer version of [name] */ 24 | val displayName: String, 25 | ) 26 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/TrackRecordDataClass.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.model.dataclass 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | data class TrackRecordDataClass( 11 | val id: Int, 12 | val mangaId: Int, 13 | val trackerId: Int, 14 | val remoteId: Long, 15 | val libraryId: Long?, 16 | val title: String, 17 | val lastChapterRead: Double, 18 | val totalChapters: Int, 19 | val status: Int, 20 | val score: Double, 21 | var scoreString: String? = null, 22 | val remoteUrl: String, 23 | val startDate: Long, 24 | val finishDate: Long, 25 | ) 26 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/TrackSearchDataClass.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.model.dataclass 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | /* 6 | * Copyright (C) Contributors to the Suwayomi project 7 | * 8 | * This Source Code Form is subject to the terms of the Mozilla Public 9 | * License, v. 2.0. If a copy of the MPL was not distributed with this 10 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 11 | 12 | @Serializable 13 | data class TrackSearchDataClass( 14 | val id: Int, 15 | val trackerId: Int, 16 | val remoteId: Long, 17 | val title: String, 18 | val totalChapters: Int, 19 | val trackingUrl: String, 20 | val coverUrl: String, 21 | val summary: String, 22 | val publishingStatus: String, 23 | val publishingType: String, 24 | val startDate: String, 25 | ) 26 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/TrackerDataClass.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.model.dataclass 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | data class TrackerDataClass( 11 | val id: Int, 12 | val name: String, 13 | val icon: String, 14 | val isLogin: Boolean, 15 | val authUrl: String?, 16 | ) 17 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryMangaTable.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.model.table 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import org.jetbrains.exposed.dao.id.IntIdTable 11 | import org.jetbrains.exposed.sql.ReferenceOption 12 | 13 | object CategoryMangaTable : IntIdTable() { 14 | val category = reference("category", CategoryTable, ReferenceOption.CASCADE) 15 | val manga = reference("manga", MangaTable, ReferenceOption.CASCADE) 16 | } 17 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/CategoryMetaTable.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.model.table 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import org.jetbrains.exposed.dao.id.IntIdTable 11 | import org.jetbrains.exposed.sql.ReferenceOption 12 | import suwayomi.tachidesk.manga.model.table.CategoryMetaTable.ref 13 | 14 | /** 15 | * Metadata storage for clients, about Category with id == [ref]. 16 | */ 17 | object CategoryMetaTable : IntIdTable() { 18 | val key = varchar("key", 256) 19 | val value = varchar("value", 4096) 20 | val ref = reference("category_ref", CategoryTable, ReferenceOption.CASCADE) 21 | } 22 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/ChapterMetaTable.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.model.table 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import org.jetbrains.exposed.dao.id.IntIdTable 11 | import org.jetbrains.exposed.sql.ReferenceOption 12 | import suwayomi.tachidesk.manga.model.table.ChapterMetaTable.ref 13 | 14 | /** 15 | * Metadata storage for clients, about Chapter with id == [ref]. 16 | * 17 | * For example, if you added reader mode(with the key juiReaderMode) such as webtoon to a manga object, 18 | * this is what will show up when you request that manga from the api again 19 | * 20 | * { 21 | * "id": 10, 22 | * "title": "Isekai manga", 23 | * "meta": { 24 | * "juiReaderMode": "webtoon" 25 | * } 26 | * } 27 | */ 28 | object ChapterMetaTable : IntIdTable() { 29 | val key = varchar("key", 256) 30 | val value = varchar("value", 4096) 31 | val ref = reference("chapter_ref", ChapterTable, ReferenceOption.CASCADE) 32 | } 33 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/MangaMetaTable.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.model.table 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import org.jetbrains.exposed.dao.id.IntIdTable 11 | import org.jetbrains.exposed.sql.ReferenceOption 12 | import suwayomi.tachidesk.manga.model.table.MangaMetaTable.ref 13 | 14 | /** 15 | * Metadata storage for clients, about Manga with id == [ref]. 16 | * 17 | * For example, if you added reader mode(with the key juiReaderMode) such as webtoon to a manga object, 18 | * this is what will show up when you request that manga from the api again 19 | * 20 | * { 21 | * "id": 10, 22 | * "title": "Isekai manga", 23 | * "meta": { 24 | * "juiReaderMode": "webtoon" 25 | * } 26 | * } 27 | */ 28 | object MangaMetaTable : IntIdTable() { 29 | val key = varchar("key", 256) 30 | val value = varchar("value", 4096) 31 | val ref = reference("manga_ref", MangaTable, ReferenceOption.CASCADE) 32 | } 33 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/PageTable.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.model.table 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import org.jetbrains.exposed.dao.id.IntIdTable 11 | import org.jetbrains.exposed.sql.ReferenceOption 12 | 13 | object PageTable : IntIdTable() { 14 | val index = integer("index") 15 | val url = varchar("url", 2048) 16 | val imageUrl = varchar("image_url", 2048).nullable() 17 | 18 | val chapter = reference("chapter", ChapterTable, ReferenceOption.CASCADE) 19 | } 20 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/SourceMetaTable.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.model.table 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import org.jetbrains.exposed.dao.id.IntIdTable 11 | import suwayomi.tachidesk.manga.model.table.ChapterMetaTable.ref 12 | 13 | /** 14 | * Metadata storage for clients, about Source with id == [ref]. 15 | */ 16 | object SourceMetaTable : IntIdTable() { 17 | val key = varchar("key", 256) 18 | val value = varchar("value", 4096) 19 | val ref = long("source_ref") 20 | } 21 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/SourceTable.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.model.table 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import org.jetbrains.exposed.dao.id.IdTable 11 | 12 | object SourceTable : IdTable() { 13 | override val id = long("id").entityId() 14 | val name = varchar("name", 128) 15 | val lang = varchar("lang", 32) 16 | val extension = reference("extension", ExtensionTable) 17 | val isNsfw = bool("is_nsfw").default(false) 18 | } 19 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/TrackRecordTable.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.model.table 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import org.jetbrains.exposed.dao.id.IntIdTable 11 | import org.jetbrains.exposed.sql.ReferenceOption 12 | 13 | object TrackRecordTable : IntIdTable() { 14 | val mangaId = reference("manga_id", MangaTable, ReferenceOption.CASCADE) 15 | val trackerId = integer("sync_id") 16 | val remoteId = long("remote_id") 17 | val libraryId = long("library_id").nullable() 18 | val title = varchar("title", 512) 19 | val lastChapterRead = double("last_chapter_read") 20 | val totalChapters = integer("total_chapters") 21 | val status = integer("status") 22 | val score = double("score") 23 | val remoteUrl = varchar("remote_url", 512) 24 | val startDate = long("start_date") 25 | val finishDate = long("finish_date") 26 | } 27 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/opds/dto/OpdsCategoryNavEntry.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.opds.dto 2 | 3 | data class OpdsCategoryNavEntry( 4 | val id: Int, 5 | val name: String, 6 | ) 7 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/opds/dto/OpdsChapterListAcqEntry.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.opds.dto 2 | 3 | data class OpdsChapterListAcqEntry( 4 | val id: Int, 5 | val mangaId: Int, 6 | val name: String, 7 | val uploadDate: Long, 8 | val chapterNumber: Float, 9 | val scanlator: String?, 10 | val read: Boolean, 11 | val lastPageRead: Int, 12 | val sourceOrder: Int, 13 | val pageCount: Int, // Can be -1 if not known 14 | ) 15 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/opds/dto/OpdsChapterMetadataAcqEntry.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.opds.dto 2 | 3 | data class OpdsChapterMetadataAcqEntry( 4 | val id: Int, 5 | val mangaId: Int, 6 | val name: String, 7 | val uploadDate: Long, 8 | val scanlator: String?, 9 | val read: Boolean, 10 | val lastPageRead: Int, 11 | val lastReadAt: Long, 12 | val sourceOrder: Int, 13 | val downloaded: Boolean, 14 | val pageCount: Int, 15 | ) 16 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/opds/dto/OpdsGenreNavEntry.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.opds.dto 2 | 3 | data class OpdsGenreNavEntry( 4 | val id: String, // Name encoded for OPDS URL (e.g., "Action%20Adventure") 5 | val title: String, // e.g., "Action & Adventure" 6 | ) 7 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/opds/dto/OpdsLanguageNavEntry.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.opds.dto 2 | 3 | data class OpdsLanguageNavEntry( 4 | val id: String, // langCode (e.g., "en") 5 | val title: String, // Localized (e.g., "English") 6 | ) 7 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/opds/dto/OpdsLibraryUpdateAcqEntry.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.opds.dto 2 | 3 | data class OpdsLibraryUpdateAcqEntry( 4 | val chapter: OpdsChapterListAcqEntry, 5 | val mangaTitle: String, 6 | val mangaAuthor: String?, 7 | val mangaId: Int, 8 | val mangaSourceLang: String?, 9 | val mangaThumbnailUrl: String?, 10 | ) 11 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/opds/dto/OpdsMangaAcqEntry.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.opds.dto 2 | 3 | data class OpdsMangaAcqEntry( 4 | val id: Int, 5 | val title: String, 6 | val author: String?, 7 | val genres: List, // Raw genres, will be processed in builder 8 | val description: String?, 9 | val thumbnailUrl: String?, // Raw thumbnail URL from DB 10 | val sourceLang: String?, 11 | val inLibrary: Boolean, 12 | ) 13 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/opds/dto/OpdsMangaDetails.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.opds.dto 2 | 3 | data class OpdsMangaDetails( // Kept name, it's specific enough 4 | val id: Int, 5 | val title: String, 6 | val thumbnailUrl: String?, 7 | val author: String?, // Added for chapter entry authors 8 | ) 9 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/opds/dto/OpdsRootNavEntry.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.opds.dto 2 | 3 | data class OpdsRootNavEntry( 4 | val id: String, 5 | val title: String, // Localized 6 | val description: String, // Localized 7 | val linkType: String, 8 | ) 9 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/opds/dto/OpdsSearchCriteria.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.opds.dto 2 | 3 | data class OpdsSearchCriteria( 4 | val query: String? = null, 5 | val author: String? = null, 6 | val title: String? = null, 7 | ) 8 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/opds/dto/OpdsSourceNavEntry.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.opds.dto 2 | 3 | data class OpdsSourceNavEntry( 4 | val id: Long, 5 | val name: String, // Not localized 6 | val iconUrl: String?, 7 | ) 8 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/opds/dto/OpdsStatusNavEntry.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.opds.dto 2 | 3 | data class OpdsStatusNavEntry( 4 | val id: Int, 5 | val title: String, // Localized 6 | ) 7 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/opds/model/OpdsAuthorXml.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.opds.model 2 | 3 | import kotlinx.serialization.Serializable 4 | import nl.adaptivity.xmlutil.serialization.XmlElement 5 | 6 | @Serializable 7 | data class OpdsAuthorXml( 8 | @XmlElement(true) 9 | val name: String, 10 | @XmlElement(true) 11 | val uri: String? = null, 12 | @XmlElement(true) 13 | val email: String? = null, 14 | ) 15 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/opds/model/OpdsCategoryXml.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.opds.model 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class OpdsCategoryXml( 7 | val scheme: String? = null, 8 | val term: String, 9 | val label: String, 10 | ) 11 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/opds/model/OpdsContentXml.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.opds.model 2 | 3 | import kotlinx.serialization.Serializable 4 | import nl.adaptivity.xmlutil.serialization.XmlValue 5 | 6 | @Serializable 7 | data class OpdsContentXml( 8 | val type: String = "text", 9 | @XmlValue(true) 10 | val value: String = "", 11 | ) 12 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/opds/model/OpdsIndirectAcquisitionXml.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.opds.model 2 | 3 | import kotlinx.serialization.Serializable 4 | import nl.adaptivity.xmlutil.serialization.XmlElement 5 | import nl.adaptivity.xmlutil.serialization.XmlSerialName 6 | import suwayomi.tachidesk.opds.constants.OpdsConstants 7 | 8 | @Serializable 9 | @XmlSerialName("indirectAcquisition", OpdsConstants.NS_OPDS, "opds") 10 | data class OpdsIndirectAcquisitionXml( 11 | val type: String, 12 | @XmlElement(true) 13 | @XmlSerialName("indirectAcquisition", OpdsConstants.NS_OPDS, "opds") 14 | val children: List? = null, 15 | ) 16 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/opds/model/OpdsSummaryXml.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.opds.model 2 | 3 | import kotlinx.serialization.Serializable 4 | import nl.adaptivity.xmlutil.serialization.XmlValue 5 | 6 | @Serializable 7 | data class OpdsSummaryXml( 8 | val type: String = "text", 9 | @XmlValue(true) 10 | val value: String = "", 11 | ) 12 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/opds/util/OpdsXmlUtil.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.opds.util 2 | 3 | import nl.adaptivity.xmlutil.XmlDeclMode 4 | import nl.adaptivity.xmlutil.core.XmlVersion 5 | import nl.adaptivity.xmlutil.serialization.XML 6 | import suwayomi.tachidesk.opds.model.OpdsFeedXml 7 | 8 | /** 9 | * Utilities for XML serialization in the OPDS context. 10 | */ 11 | object OpdsXmlUtil { 12 | /** 13 | * Configuration for the XML serializer for OPDS. 14 | */ 15 | val opdsXmlMapper: XML = 16 | XML { 17 | indent = 2 18 | xmlVersion = XmlVersion.XML10 19 | xmlDeclMode = XmlDeclMode.Charset 20 | defaultPolicy { 21 | autoPolymorphic = true 22 | } 23 | } 24 | 25 | /** 26 | * Serializes an OPDS feed to its XML string representation. 27 | * @param feed The OPDS feed to serialize 28 | * @return XML string representation of the feed 29 | */ 30 | fun serializeFeedToString(feed: OpdsFeedXml): String = opdsXmlMapper.encodeToString(OpdsFeedXml.serializer(), feed) 31 | } 32 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/ConfigAdapters.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server 2 | 3 | import org.jetbrains.exposed.sql.SortOrder 4 | 5 | interface ConfigAdapter { 6 | fun toType(configValue: String): T 7 | } 8 | 9 | object StringConfigAdapter : ConfigAdapter { 10 | override fun toType(configValue: String): String = configValue 11 | } 12 | 13 | object IntConfigAdapter : ConfigAdapter { 14 | override fun toType(configValue: String): Int = configValue.toInt() 15 | } 16 | 17 | object BooleanConfigAdapter : ConfigAdapter { 18 | override fun toType(configValue: String): Boolean = configValue.toBoolean() 19 | } 20 | 21 | object DoubleConfigAdapter : ConfigAdapter { 22 | override fun toType(configValue: String): Double = configValue.toDouble() 23 | } 24 | 25 | object SortOrderConfigAdapter : ConfigAdapter { 26 | override fun toType(configValue: String): SortOrder = SortOrder.valueOf(configValue) 27 | } 28 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/DBTransaction.util.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database 2 | 3 | import org.jetbrains.exposed.sql.Transaction 4 | import org.jetbrains.exposed.sql.transactions.TransactionManager 5 | import org.jetbrains.exposed.sql.transactions.transaction 6 | 7 | /** 8 | * Performs the given transaction block inside the parent transaction or creates a new transaction if necessary. 9 | * 10 | * Any rollback or exception in the inner transaction will be propagated to the parent transaction. 11 | */ 12 | fun dbTransaction(block: Transaction.() -> T): T { 13 | val currentTransaction = TransactionManager.currentOrNull() 14 | 15 | return if (currentTransaction == null) { 16 | transaction { block() } 17 | } else { 18 | block(currentTransaction) 19 | } 20 | } 21 | 22 | /** 23 | * Creates a nested transaction. 24 | * 25 | * Any rollback or exception will only roll back the inner (nested) transaction, leaving the parent transaction unaffected. 26 | * 27 | * Only works in case "useNestedTransactions" is enabled. 28 | */ 29 | fun nestedDbTransaction(block: Transaction.() -> T): T = 30 | transaction { 31 | check(db.useNestedTransactions) { "Nested transactions are not enabled." } 32 | block() 33 | } 34 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0002_ChapterTableIndexRename.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.RenameFieldMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0002_ChapterTableIndexRename : 14 | RenameFieldMigration( 15 | "Chapter", 16 | "number_in_list", 17 | "index", 18 | ) 19 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0003_DefaultCategory.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.RenameFieldMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0003_DefaultCategory : 14 | RenameFieldMigration( 15 | "Category", 16 | "is_landing", 17 | "is_default", 18 | ) 19 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0007_ChapterIsDownloaded.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.AddColumnMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0007_ChapterIsDownloaded : 14 | AddColumnMigration( 15 | "Chapter", 16 | "is_downloaded", 17 | "BOOLEAN", 18 | "FALSE", 19 | ) 20 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0008_ChapterPageCount.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.AddColumnMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0008_ChapterPageCount : 14 | AddColumnMigration( 15 | "Chapter", 16 | "page_count", 17 | "INT", 18 | "-1", 19 | ) 20 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0009_ChapterLastReadAt.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.AddColumnMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0009_ChapterLastReadAt : 14 | AddColumnMigration( 15 | "Chapter", 16 | "last_read_at", 17 | "BIGINT", // BIGINT == Long 18 | "0", 19 | ) 20 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0011_SourceDropPartOfFactorySource.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.DropColumnMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0011_SourceDropPartOfFactorySource : 14 | DropColumnMigration( 15 | "Source", 16 | "part_of_factory_source", 17 | ) 18 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0012_SourceIsNsfw.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.AddColumnMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0012_SourceIsNsfw : 14 | AddColumnMigration( 15 | "Source", 16 | "is_nsfw", 17 | "BOOLEAN", 18 | "FALSE", 19 | ) 20 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0013_MangaRealUrl.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.AddColumnMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0013_MangaRealUrl : 14 | AddColumnMigration( 15 | "Manga", 16 | "real_url", 17 | "VARCHAR(2048)", 18 | "NULL", 19 | ) 20 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0014_MangaRemoveLengthLimit.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.SQLMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0014_MangaRemoveLengthLimit : SQLMigration() { 14 | override val sql = 15 | """ 16 | ALTER TABLE MANGA ALTER COLUMN ARTIST VARCHAR(512); 17 | ALTER TABLE MANGA ALTER COLUMN AUTHOR VARCHAR(512); 18 | ALTER TABLE MANGA ALTER COLUMN DESCRIPTION VARCHAR; -- the default length is `Integer.MAX_VALUE` 19 | ALTER TABLE MANGA ALTER COLUMN GENRE VARCHAR; -- the default length is `Integer.MAX_VALUE` 20 | """.trimIndent() 21 | } 22 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0015_SourceAndExtensionLangAddLengthLimit.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.SQLMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0015_SourceAndExtensionLangAddLengthLimit : SQLMigration() { 14 | override val sql = 15 | """ 16 | ALTER TABLE SOURCE ALTER COLUMN LANG VARCHAR(32); 17 | ALTER TABLE EXTENSION ALTER COLUMN LANG VARCHAR(32); 18 | """.trimIndent() 19 | } 20 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0016_ChapterIndexRenameToSourceOrder.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.SQLMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0016_ChapterIndexRenameToSourceOrder : SQLMigration() { 14 | override val sql = 15 | """ 16 | ALTER TABLE CHAPTER ALTER COLUMN INDEX RENAME TO SOURCE_ORDER; 17 | """.trimIndent() 18 | } 19 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0017_ChapterFetchedAt.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.AddColumnMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0017_ChapterFetchedAt : 14 | AddColumnMigration( 15 | "Chapter", 16 | "fetched_at", 17 | "BIGINT", 18 | "0", 19 | ) 20 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0018_MangaInLibraryAt.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.AddColumnMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0018_MangaInLibraryAt : 14 | AddColumnMigration( 15 | "Manga", 16 | "in_library_at", 17 | "BIGINT", 18 | "0", 19 | ) 20 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0019_RemoveAnime.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("ktlint:standard:property-naming") 2 | 3 | package suwayomi.tachidesk.server.database.migration 4 | 5 | /* 6 | * Copyright (C) Contributors to the Suwayomi project 7 | * 8 | * This Source Code Form is subject to the terms of the Mozilla Public 9 | * License, v. 2.0. If a copy of the MPL was not distributed with this 10 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 11 | 12 | import de.neonew.exposed.migrations.helpers.SQLMigration 13 | 14 | @Suppress("ClassName", "unused") 15 | class M0019_RemoveAnime : SQLMigration() { 16 | val Anime = "ANIME" 17 | val AnimeExtension = "ANIMEEXTENSION" 18 | val AnimeSource = "ANIMESOURCE" 19 | val Episode = "EPISODE" 20 | 21 | override val sql = 22 | """ 23 | DROP TABLE $AnimeSource; 24 | DROP TABLE $AnimeExtension; 25 | DROP TABLE $Episode; 26 | DROP TABLE $Anime; 27 | """.trimIndent() 28 | } 29 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0020_AddMangaLastFetchedAtColumns.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.SQLMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0020_AddMangaLastFetchedAtColumns : SQLMigration() { 14 | override val sql = 15 | """ 16 | ALTER TABLE Manga ADD COLUMN last_fetched_at BIGINT DEFAULT 0; 17 | ALTER TABLE Manga ADD COLUMN chapters_last_fetched_at BIGINT DEFAULT 0; 18 | """.trimIndent() 19 | } 20 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0022_MangaThumbnailLastFetched.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.AddColumnMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0022_MangaThumbnailLastFetched : 14 | AddColumnMigration( 15 | "Manga", 16 | "thumbnail_url_last_fetched", 17 | "BIGINT", 18 | "0", 19 | ) 20 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0024_MangaUpdateStrategy.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.AddColumnMigration 11 | import eu.kanade.tachiyomi.source.model.UpdateStrategy 12 | 13 | @Suppress("ClassName", "unused") 14 | class M0024_MangaUpdateStrategy : 15 | AddColumnMigration( 16 | "Manga", 17 | "update_strategy", 18 | "VARCHAR(256)", 19 | "'${UpdateStrategy.ALWAYS_UPDATE.name}'", 20 | ) 21 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0025_ChapterRealUrl.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.AddColumnMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0025_ChapterRealUrl : 14 | AddColumnMigration( 15 | "Chapter", 16 | "real_url", 17 | "VARCHAR(2048)", 18 | "NULL", 19 | ) 20 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0026_CategoryIncludeInUpdate.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.AddColumnMigration 11 | import suwayomi.tachidesk.manga.model.dataclass.IncludeOrExclude 12 | 13 | @Suppress("ClassName", "unused") 14 | class M0026_CategoryIncludeInUpdate : 15 | AddColumnMigration( 16 | "Category", 17 | "include_in_update", 18 | "INT", 19 | IncludeOrExclude.UNSET.value.toString(), 20 | ) 21 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0027_AddDefaultCategory.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | import de.neonew.exposed.migrations.helpers.SQLMigration 4 | 5 | /* 6 | * Copyright (C) Contributors to the Suwayomi project 7 | * 8 | * This Source Code Form is subject to the terms of the Mozilla Public 9 | * License, v. 2.0. If a copy of the MPL was not distributed with this 10 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0027_AddDefaultCategory : SQLMigration() { 14 | override val sql: String = 15 | """ 16 | INSERT INTO CATEGORY (ID, NAME, IS_DEFAULT, "ORDER", INCLUDE_IN_UPDATE) VALUES (0, 'Default', TRUE, 0, -1) 17 | """.trimIndent() 18 | } 19 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0029_DropMangaDefaultCategory.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.DropColumnMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0029_DropMangaDefaultCategory : 14 | DropColumnMigration( 15 | "Manga", 16 | "default_category", 17 | ) 18 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0030_FixDateUpload.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.SQLMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0030_FixDateUpload : SQLMigration() { 14 | // language=h2 15 | override val sql = 16 | """ 17 | UPDATE CHAPTER 18 | SET DATE_UPLOAD = (FETCHED_AT * 1000) 19 | WHERE DATE_UPLOAD = 0; 20 | """.trimIndent() 21 | } 22 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0031_AddExtensionRepo.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.AddColumnMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0031_AddExtensionRepo : 14 | AddColumnMigration( 15 | "Extension", 16 | "repo", 17 | "VARCHAR(1024)", 18 | "NULL", 19 | ) 20 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0032_FixExtensionRepos.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.SQLMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0032_FixExtensionRepos : SQLMigration() { 14 | // language=h2 15 | override val sql = 16 | """ 17 | UPDATE EXTENSION 18 | SET REPO = 'https://raw.githubusercontent.com/tachiyomiorg/tachiyomi-extensions/repo/'; 19 | """.trimIndent() 20 | } 21 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0034_CategoryIncludeInDownload.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.AddColumnMigration 11 | import suwayomi.tachidesk.manga.model.dataclass.IncludeOrExclude 12 | 13 | @Suppress("ClassName", "unused") 14 | class M0034_CategoryIncludeInDownload : 15 | AddColumnMigration( 16 | "Category", 17 | "include_in_download", 18 | "INT", 19 | IncludeOrExclude.UNSET.value.toString(), 20 | ) 21 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0036_SourceMeta.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.AddTableMigration 11 | import org.jetbrains.exposed.dao.id.IntIdTable 12 | import org.jetbrains.exposed.sql.Table 13 | 14 | @Suppress("ClassName", "unused") 15 | class M0036_SourceMeta : AddTableMigration() { 16 | private class SourceMetaTable : IntIdTable() { 17 | val key = varchar("key", 256) 18 | val value = varchar("value", 4096) 19 | val ref = long("source_ref") 20 | } 21 | 22 | override val tables: Array 23 | get() = 24 | arrayOf( 25 | SourceMetaTable(), 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0037_RemoveTrackRecordsOfUnsupportedTrackers.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.SQLMigration 11 | import suwayomi.tachidesk.manga.impl.track.tracker.TrackerManager 12 | 13 | @Suppress("ClassName", "unused") 14 | class M0037_RemoveTrackRecordsOfUnsupportedTrackers : SQLMigration() { 15 | override val sql: String = 16 | """ 17 | DELETE FROM TRACKRECORD WHERE SYNC_ID NOT IN (${TrackerManager.MYANIMELIST}, ${TrackerManager.ANILIST}, ${TrackerManager.MANGA_UPDATES}) 18 | """.trimIndent() 19 | } 20 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0038_RemoveTrackRecordsOfUnsupportedTrackersII.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.SQLMigration 11 | import suwayomi.tachidesk.manga.impl.track.tracker.TrackerManager 12 | 13 | @Suppress("ClassName", "unused") 14 | class M0038_RemoveTrackRecordsOfUnsupportedTrackersII : SQLMigration() { 15 | override val sql: String = 16 | """ 17 | DELETE FROM TRACKRECORD WHERE SYNC_ID NOT IN (${TrackerManager.MYANIMELIST}, ${TrackerManager.ANILIST}, ${TrackerManager.MANGA_UPDATES}) 18 | """.trimIndent() 19 | } 20 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0039_DeleteDuplicatedChapters.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.SQLMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0039_DeleteDuplicatedChapters : SQLMigration() { 14 | override val sql: String = 15 | """ 16 | DELETE FROM CHAPTER 17 | WHERE id NOT IN ( 18 | SELECT MIN(id) 19 | FROM CHAPTER 20 | GROUP BY URL, MANGA 21 | ); 22 | """.trimIndent() 23 | } 24 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0040_AddUniqueConstraintToChapterTable.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.SQLMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0040_AddUniqueConstraintToChapterTable : SQLMigration() { 14 | override val sql: String = 15 | """ 16 | ALTER TABLE CHAPTER 17 | ADD CONSTRAINT UC_CHAPTER UNIQUE (URL, MANGA) 18 | """.trimIndent() 19 | } 20 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0041_FixDownloadedChaptersWithoutPageCount.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.SQLMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0041_FixDownloadedChaptersWithoutPageCount : SQLMigration() { 14 | override val sql: String = 15 | """ 16 | UPDATE CHAPTER 17 | SET IS_DOWNLOADED = FALSE 18 | WHERE IS_DOWNLOADED = TRUE AND PAGE_COUNT <= 0 19 | """.trimIndent() 20 | } 21 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0042_MangaRemoveLengthLimit_II.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.SQLMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0042_MangaRemoveLengthLimit_II : SQLMigration() { 14 | override val sql = 15 | """ 16 | ALTER TABLE MANGA ALTER COLUMN ARTIST VARCHAR; -- the default length is `Integer.MAX_VALUE` 17 | ALTER TABLE MANGA ALTER COLUMN AUTHOR VARCHAR; -- the default length is `Integer.MAX_VALUE` 18 | """.trimIndent() 19 | } 20 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0043_PreventNegativeLastPageRead.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.SQLMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0043_PreventNegativeLastPageRead : SQLMigration() { 14 | override val sql: String = 15 | """ 16 | UPDATE CHAPTER 17 | SET LAST_PAGE_READ = 0 18 | WHERE LAST_PAGE_READ < 0; 19 | 20 | ALTER TABLE CHAPTER 21 | ADD CONSTRAINT CHK_LAST_READ_PAGE_POSITIVE CHECK (LAST_PAGE_READ >= 0) 22 | """.trimIndent() 23 | } 24 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0044_FixDownloadedChaptersWithoutPageCountII.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.SQLMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0044_FixDownloadedChaptersWithoutPageCountII : SQLMigration() { 14 | override val sql: String = 15 | """ 16 | UPDATE CHAPTER 17 | SET IS_DOWNLOADED = FALSE 18 | WHERE IS_DOWNLOADED = TRUE AND PAGE_COUNT <= 0 19 | """.trimIndent() 20 | } 21 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0045_PreventDuplicatedChapterPages.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.SQLMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0045_PreventDuplicatedChapterPages : SQLMigration() { 14 | override val sql: String = 15 | """ 16 | DELETE FROM PAGE 17 | WHERE ID NOT IN ( 18 | SELECT MIN(ID) 19 | FROM PAGE 20 | GROUP BY INDEX, "imageUrl", CHAPTER 21 | ); 22 | 23 | ALTER TABLE PAGE 24 | ADD CONSTRAINT UC_PAGE UNIQUE (INDEX, "imageUrl", CHAPTER); 25 | """.trimIndent() 26 | } 27 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/M0046_RenamePageImageUrlColumn.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.database.migration 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import de.neonew.exposed.migrations.helpers.RenameFieldMigration 11 | 12 | @Suppress("ClassName", "unused") 13 | class M0046_RenamePageImageUrlColumn : 14 | RenameFieldMigration( 15 | "Page", 16 | "\"imageUrl\"", 17 | "image_url", 18 | ) 19 | -------------------------------------------------------------------------------- /server/src/main/kotlin/suwayomi/tachidesk/server/util/AppExit.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.server.util 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import io.github.oshai.kotlinlogging.KotlinLogging 11 | import kotlin.system.exitProcess 12 | 13 | private val logger = KotlinLogging.logger {} 14 | 15 | enum class ExitCode( 16 | val code: Int, 17 | ) { 18 | Success(0), 19 | MutexCheckFailedTachideskRunning(1), 20 | MutexCheckFailedAnotherAppRunning(2), 21 | WebUISetupFailure(3), 22 | } 23 | 24 | fun shutdownApp(exitCode: ExitCode) { 25 | logger.info { "Shutting Down Suwayomi-Server. Goodbye!" } 26 | 27 | exitProcess(exitCode.code) 28 | } 29 | -------------------------------------------------------------------------------- /server/src/main/resources/icon/faviconlogo-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Suwayomi-Server/dee61e191c0175c6dae6346f58293e0225df8b1a/server/src/main/resources/icon/faviconlogo-128.png -------------------------------------------------------------------------------- /server/src/main/resources/icon/faviconlogo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Suwayomi-Server/dee61e191c0175c6dae6346f58293e0225df8b1a/server/src/main/resources/icon/faviconlogo.ico -------------------------------------------------------------------------------- /server/src/main/resources/icon/faviconlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Suwayomi-Server/dee61e191c0175c6dae6346f58293e0225df8b1a/server/src/main/resources/icon/faviconlogo.png -------------------------------------------------------------------------------- /server/src/main/resources/icon/localSource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Suwayomi-Server/dee61e191c0175c6dae6346f58293e0225df8b1a/server/src/main/resources/icon/localSource.png -------------------------------------------------------------------------------- /server/src/main/resources/static/tracker/anilist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Suwayomi-Server/dee61e191c0175c6dae6346f58293e0225df8b1a/server/src/main/resources/static/tracker/anilist.png -------------------------------------------------------------------------------- /server/src/main/resources/static/tracker/bangumi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Suwayomi-Server/dee61e191c0175c6dae6346f58293e0225df8b1a/server/src/main/resources/static/tracker/bangumi.png -------------------------------------------------------------------------------- /server/src/main/resources/static/tracker/kitsu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Suwayomi-Server/dee61e191c0175c6dae6346f58293e0225df8b1a/server/src/main/resources/static/tracker/kitsu.png -------------------------------------------------------------------------------- /server/src/main/resources/static/tracker/mal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Suwayomi-Server/dee61e191c0175c6dae6346f58293e0225df8b1a/server/src/main/resources/static/tracker/mal.png -------------------------------------------------------------------------------- /server/src/main/resources/static/tracker/manga_updates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Suwayomi-Server/dee61e191c0175c6dae6346f58293e0225df8b1a/server/src/main/resources/static/tracker/manga_updates.png -------------------------------------------------------------------------------- /server/src/test/kotlin/suwayomi/tachidesk/ImageUtilTest.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk 2 | 3 | import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil 4 | import kotlin.test.Test 5 | import kotlin.test.assertEquals 6 | 7 | class ImageUtilTest { 8 | @Test 9 | fun jxlTest() { 10 | val type = ImageUtil.findImageType(this::class.java.classLoader.getResourceAsStream("dice.jxl")!!) 11 | assertEquals(ImageUtil.ImageType.JXL, type) 12 | } 13 | 14 | @Test 15 | fun avifTest() { 16 | val type = ImageUtil.findImageType(this::class.java.classLoader.getResourceAsStream("fox.profile0.8bpc.yuv420.avif")!!) 17 | assertEquals(ImageUtil.ImageType.AVIF, type) 18 | } 19 | 20 | @Test 21 | fun heifTest() { 22 | val type = ImageUtil.findImageType(this::class.java.classLoader.getResourceAsStream("sample1.heif")!!) 23 | assertEquals(ImageUtil.ImageType.HEIF, type) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /server/src/test/kotlin/suwayomi/tachidesk/PathTest.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk 2 | 3 | import kotlin.io.path.Path 4 | import kotlin.test.Test 5 | import kotlin.test.assertEquals 6 | import kotlin.test.assertFalse 7 | 8 | class PathTest { 9 | @Test 10 | fun testCanonicalPath() { 11 | val path = Path("/test/path/") 12 | assert(path.resolve("child/path").startsWith(path)) 13 | assertFalse(path.resolve("../parent/child/path").normalize().startsWith(path)) 14 | } 15 | 16 | @Test 17 | fun testParentPath() { 18 | val path = Path("/test/path/") 19 | assertEquals(path.resolve("child.txt").parent, path) 20 | assertEquals(path.resolve("child.txt").normalize().parent, path) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /server/src/test/kotlin/suwayomi/tachidesk/manga/impl/PageTest.kt: -------------------------------------------------------------------------------- 1 | package suwayomi.tachidesk.manga.impl 2 | 3 | /* 4 | * Copyright (C) Contributors to the Suwayomi project 5 | * 6 | * This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 9 | 10 | import org.junit.jupiter.api.Test 11 | import suwayomi.tachidesk.manga.impl.Page.getPageName 12 | import suwayomi.tachidesk.test.ApplicationTest 13 | import kotlin.test.assertEquals 14 | 15 | class PageTest : ApplicationTest() { 16 | @Test 17 | fun testGetPageName() { 18 | val tests = listOf(0 to 5, 1 to 5, 2 to 5, 100 to 100, 998 to 1000, 1400 to 1500) 19 | 20 | val testResults = 21 | tests.map { (page, count) -> 22 | getPageName(page, count) 23 | } 24 | 25 | assertEquals(testResults[0], "001") 26 | assertEquals(testResults[1], "002") 27 | assertEquals(testResults[2], "003") 28 | assertEquals(testResults[3], "101") 29 | assertEquals(testResults[4], "0999") 30 | assertEquals(testResults[5], "1401") 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /server/src/test/resources/dice.jxl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Suwayomi-Server/dee61e191c0175c6dae6346f58293e0225df8b1a/server/src/test/resources/dice.jxl -------------------------------------------------------------------------------- /server/src/test/resources/fox.profile0.8bpc.yuv420.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Suwayomi-Server/dee61e191c0175c6dae6346f58293e0225df8b1a/server/src/test/resources/fox.profile0.8bpc.yuv420.avif -------------------------------------------------------------------------------- /server/src/test/resources/sample1.heif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Suwayomi-Server/dee61e191c0175c6dae6346f58293e0225df8b1a/server/src/test/resources/sample1.heif -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = System.getenv("ProductName") ?: "Suwayomi-Server" 2 | 3 | include("server") 4 | include("server:i18n") 5 | 6 | include("AndroidCompat") 7 | include("AndroidCompat:Config") 8 | 9 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") 10 | --------------------------------------------------------------------------------