added TMDB

This commit is contained in:
cranci1 2025-03-05 13:07:44 +01:00
parent eb65912998
commit 9af4984cc6
8 changed files with 342 additions and 7 deletions

View file

@ -0,0 +1,57 @@
//
// TMDB-Seasonal.swift
// Sulfur
//
// Created by Francesco on 05/03/25.
//
import Foundation
class TMDBSeasonal {
static func fetchTMDBSeasonal(completion: @escaping ([AniListItem]?) -> Void) {
Task {
do {
let url = URL(string: "https://api.themoviedb.org/3/movie/upcoming")!
var components = URLComponents(url: url, resolvingAgainstBaseURL: true)!
components.queryItems = [
URLQueryItem(name: "language", value: "en-US"),
URLQueryItem(name: "page", value: "1"),
]
var request = URLRequest(url: components.url!)
request.httpMethod = "GET"
request.timeoutInterval = 10
request.allHTTPHeaderFields = [
"accept": "application/json",
"Authorization": "Bearer \(TMBDRequest.decryptToken())"
]
let (data, _) = try await URLSession.custom.data(for: request)
let response = try JSONDecoder().decode(TMDBResponse.self, from: data)
let anilistItems = response.results.map { item in
AniListItem(
id: item.id,
title: AniListTitle(
romaji: item.displayTitle,
english: item.originalTitle ?? item.originalName ?? item.displayTitle,
native: ""
),
coverImage: AniListCoverImage(
large: item.posterURL
)
)
}
DispatchQueue.main.async {
completion(anilistItems)
}
} catch {
DispatchQueue.main.async {
Logger.shared.log("Error fetching TMDB seasonal: \(error.localizedDescription)")
completion(nil)
}
}
}
}
}

View file

@ -0,0 +1,62 @@
//
// TMDB-Trending.swift
// Sulfur
//
// Created by Francesco on 05/03/25.
//
import Foundation
class TMBDTrending {
static func fetchTMDBTrending(completion: @escaping ([AniListItem]?) -> Void) {
Task {
do {
let items = try await fetchTrendingItems()
let anilistItems = items.map { item in
AniListItem(
id: item.id,
title: AniListTitle(
romaji: item.displayTitle,
english: item.originalTitle ?? item.originalName ?? item.displayTitle,
native: ""
),
coverImage: AniListCoverImage(
large: item.posterURL
)
)
}
DispatchQueue.main.async {
completion(anilistItems)
}
} catch {
DispatchQueue.main.async {
Logger.shared.log("Error fetching TMDB trending: \(error.localizedDescription)")
completion(nil)
}
}
}
}
private static func fetchTrendingItems() async throws -> [TMDBItem] {
let url = URL(string: "https://api.themoviedb.org/3/trending/all/day")!
var components = URLComponents(url: url, resolvingAgainstBaseURL: true)!
let queryItems: [URLQueryItem] = [
URLQueryItem(name: "language", value: "en-US")
]
components.queryItems = queryItems
var request = URLRequest(url: components.url!)
request.httpMethod = "GET"
request.timeoutInterval = 10
request.allHTTPHeaderFields = [
"accept": "application/json",
"Authorization": "Bearer \(TMBDRequest.decryptToken())"
]
let (data, _) = try await URLSession.custom.data(for: request)
let response = try JSONDecoder().decode(TMDBResponse.self, from: data)
return response.results
}
}

View file

@ -0,0 +1,61 @@
//
// TMDBItem.swift
// Sulfur
//
// Created by Francesco on 05/03/25.
//
import Foundation
struct TMDBItem: Codable {
let id: Int
let mediaType: String?
let title: String?
let originalTitle: String?
let releaseDate: String?
let name: String?
let originalName: String?
let firstAirDate: String?
let posterPath: String?
let backdropPath: String?
let overview: String
let voteAverage: Double?
enum CodingKeys: String, CodingKey {
case id, overview
case mediaType = "media_type"
case title, name
case originalTitle = "original_title"
case originalName = "original_name"
case posterPath = "poster_path"
case backdropPath = "backdrop_path"
case releaseDate = "release_date"
case firstAirDate = "first_air_date"
case voteAverage = "vote_average"
}
var displayTitle: String {
return title ?? name ?? "Unknown Title"
}
var posterURL: String {
if let path = posterPath {
return "https://image.tmdb.org/t/p/w500\(path)"
}
return ""
}
var backdropURL: String {
if let path = backdropPath {
return "https://image.tmdb.org/t/p/original\(path)"
}
return ""
}
var displayDate: String {
return releaseDate ?? firstAirDate ?? ""
}
}

View file

@ -0,0 +1,41 @@
//
// TMDBRequest.swift
// Sulfur
//
// Created by Francesco on 05/03/25.
//
import Foundation
struct TMDBResponse: Codable {
let results: [TMDBItem]
let page: Int
let totalPages: Int
let totalResults: Int
enum CodingKeys: String, CodingKey {
case results, page
case totalPages = "total_pages"
case totalResults = "total_results"
}
}
class TMBDRequest {
static let encodedTokenParts = [
"XZXlKaGJHY2lPaUpJVXpJMU5pSjk=",
"XZXlKaGRXUWlPaUkzTXpoaU5HVmtaREJoTVRVMlkyTXhNalprWXpSaE5HSTRZV1ZoTkdGallTSXNJbTVpWmlJNk1UYzBNVEUzTXpjd01pNDNPRGN3TURJc0luTjFZaUk2SWpZM1l6Z3pNMk0yWkRjME1UbGpaR1prT0RabE1tUmtaaUlzSW5OamIzQmxjeUk2V3lKaGNHbGZjbVZoWkNKZExDSjJaWEp6YVc5dUlqb3hmUT09",
"XR2ZlN0YtOENXSlhnT052MzRtZzNqSFhmTDZCeGJqLWhBWWY5ZllpOUNrRQ=="
]
static func decryptToken() -> String {
let decodedParts = encodedTokenParts.map { part -> String in
let cleanPart = String(part.dropFirst(1))
guard let data = Data(base64Encoded: cleanPart) else {
return ""
}
return String(data: data, encoding: .utf8) ?? ""
}
return decodedParts.joined()
}
}

View file

@ -9,6 +9,7 @@ import SwiftUI
import Kingfisher
struct HomeView: View {
@AppStorage("trackingService") private var tracingService: String = "AniList"
@State private var aniListItems: [AniListItem] = []
@State private var trendingItems: [AniListItem] = []
@State private var continueWatchingItems: [ContinueWatchingItem] = []
@ -257,14 +258,28 @@ struct HomeView: View {
}
.onAppear {
continueWatchingItems = ContinueWatchingManager.shared.fetchItems()
AnilistServiceSeasonalAnime().fetchSeasonalAnime { items in
if let items = items {
aniListItems = items
if tracingService == "TMDB" {
TMDBSeasonal.fetchTMDBSeasonal { items in
if let items = items {
aniListItems = items
}
}
}
AnilistServiceTrendingAnime().fetchTrendingAnime { items in
if let items = items {
trendingItems = items
TMBDTrending.fetchTMDBTrending { items in
if let items = items {
trendingItems = items
}
}
} else {
AnilistServiceSeasonalAnime().fetchSeasonalAnime { items in
if let items = items {
aniListItems = items
}
}
AnilistServiceTrendingAnime().fetchTrendingAnime { items in
if let items = items {
trendingItems = items
}
}
}
}

View file

@ -0,0 +1,51 @@
//
// SettingsViewTrackingServices.swift
// Sulfur
//
// Created by Francesco on 05/03/25.
//
import SwiftUI
import Kingfisher
struct SettingsViewTrackingServices: View {
@AppStorage("trackingService") private var trackingService: String = "AniList"
@EnvironmentObject var settings: Settings
var body: some View {
Form {
Section(header: Text("Tracking Service")) {
HStack {
Text("Service")
Spacer()
Menu {
Button(action: { trackingService = "AniList" }) {
HStack {
KFImage(URL(string: "https://avatars.githubusercontent.com/u/18018524?s=280&v=4"))
.resizable()
.frame(width: 20, height: 20)
Text("AniList")
}
}
Button(action: { trackingService = "TMDB" }) {
HStack {
KFImage(URL(string: "https://pbs.twimg.com/profile_images/1243623122089041920/gVZIvphd_400x400.jpg"))
.resizable()
.frame(width: 20, height: 20)
Text("TMDB")
}
}
} label: {
HStack {
KFImage(URL(string: trackingService == "TMDB" ? "https://pbs.twimg.com/profile_images/1243623122089041920/gVZIvphd_400x400.jpg" : "https://avatars.githubusercontent.com/u/18018524?s=280&v=4"))
.resizable()
.frame(width: 20, height: 20)
Text(trackingService)
}
}
}
}
}
.navigationTitle("Tracking Service")
}
}

View file

@ -21,6 +21,9 @@ struct SettingsView: View {
NavigationLink(destination: SettingsViewModule()) {
Text("Modules")
}
NavigationLink(destination: SettingsViewTrackingServices()) {
Text("Tracking Services")
}
}
Section(header: Text("Info")) {
@ -73,6 +76,7 @@ struct SettingsView: View {
}
}
}
Section(footer: Text("Running Sora 0.2.1")) {}
}
.navigationTitle("Settings")
}

View file

@ -16,6 +16,11 @@
131845F92D47C62D00CA7A54 /* SettingsViewGeneral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 131845F82D47C62D00CA7A54 /* SettingsViewGeneral.swift */; };
1327FBA72D758CEA00FC6689 /* Analytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1327FBA62D758CEA00FC6689 /* Analytics.swift */; };
1327FBA92D758DEA00FC6689 /* UIDevice+Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1327FBA82D758DEA00FC6689 /* UIDevice+Model.swift */; };
1334FF4D2D786C93007E289F /* TMDB-Seasonal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1334FF4C2D786C93007E289F /* TMDB-Seasonal.swift */; };
1334FF4F2D786C9E007E289F /* TMDB-Trending.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1334FF4E2D786C9E007E289F /* TMDB-Trending.swift */; };
1334FF522D7871B7007E289F /* TMDBItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1334FF512D7871B7007E289F /* TMDBItem.swift */; };
1334FF542D787217007E289F /* TMDBRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1334FF532D787217007E289F /* TMDBRequest.swift */; };
1334FF562D7872E9007E289F /* SettingsViewTrackingServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1334FF552D7872E9007E289F /* SettingsViewTrackingServices.swift */; };
133D7C6E2D2BE2500075467E /* SoraApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133D7C6D2D2BE2500075467E /* SoraApp.swift */; };
133D7C702D2BE2500075467E /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133D7C6F2D2BE2500075467E /* ContentView.swift */; };
133D7C722D2BE2520075467E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 133D7C712D2BE2520075467E /* Assets.xcassets */; };
@ -67,6 +72,11 @@
131845F82D47C62D00CA7A54 /* SettingsViewGeneral.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewGeneral.swift; sourceTree = "<group>"; };
1327FBA62D758CEA00FC6689 /* Analytics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Analytics.swift; sourceTree = "<group>"; };
1327FBA82D758DEA00FC6689 /* UIDevice+Model.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIDevice+Model.swift"; sourceTree = "<group>"; };
1334FF4C2D786C93007E289F /* TMDB-Seasonal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TMDB-Seasonal.swift"; sourceTree = "<group>"; };
1334FF4E2D786C9E007E289F /* TMDB-Trending.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TMDB-Trending.swift"; sourceTree = "<group>"; };
1334FF512D7871B7007E289F /* TMDBItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TMDBItem.swift; sourceTree = "<group>"; };
1334FF532D787217007E289F /* TMDBRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TMDBRequest.swift; sourceTree = "<group>"; };
1334FF552D7872E9007E289F /* SettingsViewTrackingServices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewTrackingServices.swift; sourceTree = "<group>"; };
133D7C6A2D2BE2500075467E /* Sulfur.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sulfur.app; sourceTree = BUILT_PRODUCTS_DIR; };
133D7C6D2D2BE2500075467E /* SoraApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoraApp.swift; sourceTree = "<group>"; };
133D7C6F2D2BE2500075467E /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
@ -123,6 +133,7 @@
13103E802D589D6C000F0673 /* Tracking Services */ = {
isa = PBXGroup;
children = (
1334FF4A2D786C6D007E289F /* TMDB */,
13103E812D589D77000F0673 /* AniList */,
);
path = "Tracking Services";
@ -173,6 +184,33 @@
path = Analytics;
sourceTree = "<group>";
};
1334FF4A2D786C6D007E289F /* TMDB */ = {
isa = PBXGroup;
children = (
1334FF502D7871A4007E289F /* Struct */,
1334FF4B2D786C81007E289F /* HomePage */,
);
path = TMDB;
sourceTree = "<group>";
};
1334FF4B2D786C81007E289F /* HomePage */ = {
isa = PBXGroup;
children = (
1334FF4C2D786C93007E289F /* TMDB-Seasonal.swift */,
1334FF4E2D786C9E007E289F /* TMDB-Trending.swift */,
);
path = HomePage;
sourceTree = "<group>";
};
1334FF502D7871A4007E289F /* Struct */ = {
isa = PBXGroup;
children = (
1334FF512D7871B7007E289F /* TMDBItem.swift */,
1334FF532D787217007E289F /* TMDBRequest.swift */,
);
path = Struct;
sourceTree = "<group>";
};
133D7C612D2BE2500075467E = {
isa = PBXGroup;
children = (
@ -243,6 +281,7 @@
131845F82D47C62D00CA7A54 /* SettingsViewGeneral.swift */,
135CCBE12D4D1138008B9C0E /* SettingsViewPlayer.swift */,
130C6BF92D53AB1F00DC1432 /* SettingsViewData.swift */,
1334FF552D7872E9007E289F /* SettingsViewTrackingServices.swift */,
);
path = SettingsSubViews;
sourceTree = "<group>";
@ -475,14 +514,18 @@
13B7F4C12D58FFDD0045714A /* Shimmer.swift in Sources */,
139935662D468C450065CEFF /* ModuleManager.swift in Sources */,
133D7C902D2BE2640075467E /* SettingsView.swift in Sources */,
1334FF4F2D786C9E007E289F /* TMDB-Trending.swift in Sources */,
13CBEFDA2D5F7D1200D011EE /* String.swift in Sources */,
130C6BFA2D53AB1F00DC1432 /* SettingsViewData.swift in Sources */,
13EA2BD72D32D97400C1EBD7 /* MusicProgressSlider.swift in Sources */,
1334FF542D787217007E289F /* TMDBRequest.swift in Sources */,
1E9FF1D32D403E49008AC100 /* SettingsViewLoggerFilter.swift in Sources */,
13EA2BD92D32D98400C1EBD7 /* NormalPlayer.swift in Sources */,
133D7C932D2BE2640075467E /* Modules.swift in Sources */,
1334FF562D7872E9007E289F /* SettingsViewTrackingServices.swift in Sources */,
136F21B92D5B8DD8006409AC /* AniList-MediaInfo.swift in Sources */,
133D7C702D2BE2500075467E /* ContentView.swift in Sources */,
1334FF522D7871B7007E289F /* TMDBItem.swift in Sources */,
13D99CF72D4E73C300250A86 /* ModuleAdditionSettingsView.swift in Sources */,
13C0E5EC2D5F85F800E7F619 /* ContinueWatchingItem.swift in Sources */,
13CBA0882D60F19C00EFE70A /* VTTSubtitlesLoader.swift in Sources */,
@ -507,6 +550,7 @@
133D7C8E2D2BE2640075467E /* LibraryView.swift in Sources */,
133D7C6E2D2BE2500075467E /* SoraApp.swift in Sources */,
138AA1B92D2D66FD0021F9DF /* CircularProgressBar.swift in Sources */,
1334FF4D2D786C93007E289F /* TMDB-Seasonal.swift in Sources */,
13EA2BD52D32D97400C1EBD7 /* CustomPlayer.swift in Sources */,
1399FAD42D3AB38C00E97C31 /* SettingsViewLogger.swift in Sources */,
13C0E5EA2D5F85EA00E7F619 /* ContinueWatchingManager.swift in Sources */,