mirror of
https://github.com/cranci1/Sora.git
synced 2026-04-25 02:13:31 +00:00
New Analytics feature
This commit is contained in:
parent
37a596fc8e
commit
80e82e60ae
6 changed files with 280 additions and 0 deletions
|
|
@ -51,6 +51,8 @@
|
||||||
13EA2BD72D32D97400C1EBD7 /* MusicProgressSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EA2BD42D32D97400C1EBD7 /* MusicProgressSlider.swift */; };
|
13EA2BD72D32D97400C1EBD7 /* MusicProgressSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EA2BD42D32D97400C1EBD7 /* MusicProgressSlider.swift */; };
|
||||||
13EA2BD92D32D98400C1EBD7 /* NormalPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EA2BD82D32D98400C1EBD7 /* NormalPlayer.swift */; };
|
13EA2BD92D32D98400C1EBD7 /* NormalPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EA2BD82D32D98400C1EBD7 /* NormalPlayer.swift */; };
|
||||||
1E9FF1D32D403E49008AC100 /* SettingsViewLoggerFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E9FF1D22D403E42008AC100 /* SettingsViewLoggerFilter.swift */; };
|
1E9FF1D32D403E49008AC100 /* SettingsViewLoggerFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E9FF1D22D403E42008AC100 /* SettingsViewLoggerFilter.swift */; };
|
||||||
|
730C73562D726DAB001EC57C /* Analytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 730C73552D726DA5001EC57C /* Analytics.swift */; };
|
||||||
|
7374EF382D74AEF400319CEC /* UIDevice+Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7374EF372D74AEEA00319CEC /* UIDevice+Model.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
|
@ -99,6 +101,8 @@
|
||||||
13EA2BD42D32D97400C1EBD7 /* MusicProgressSlider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MusicProgressSlider.swift; sourceTree = "<group>"; };
|
13EA2BD42D32D97400C1EBD7 /* MusicProgressSlider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MusicProgressSlider.swift; sourceTree = "<group>"; };
|
||||||
13EA2BD82D32D98400C1EBD7 /* NormalPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NormalPlayer.swift; sourceTree = "<group>"; };
|
13EA2BD82D32D98400C1EBD7 /* NormalPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NormalPlayer.swift; sourceTree = "<group>"; };
|
||||||
1E9FF1D22D403E42008AC100 /* SettingsViewLoggerFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewLoggerFilter.swift; sourceTree = "<group>"; };
|
1E9FF1D22D403E42008AC100 /* SettingsViewLoggerFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewLoggerFilter.swift; sourceTree = "<group>"; };
|
||||||
|
730C73552D726DA5001EC57C /* Analytics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Analytics.swift; sourceTree = "<group>"; };
|
||||||
|
7374EF372D74AEEA00319CEC /* UIDevice+Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDevice+Model.swift"; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
|
@ -236,6 +240,7 @@
|
||||||
133D7C852D2BE2640075467E /* Utils */ = {
|
133D7C852D2BE2640075467E /* Utils */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
730C73542D726D83001EC57C /* Analytics */,
|
||||||
13C0E5E82D5F85DD00E7F619 /* ContinueWatching */,
|
13C0E5E82D5F85DD00E7F619 /* ContinueWatching */,
|
||||||
13103E8C2D58E037000F0673 /* SkeletonCells */,
|
13103E8C2D58E037000F0673 /* SkeletonCells */,
|
||||||
13DC0C442D302C6A00D0F966 /* MediaPlayer */,
|
13DC0C442D302C6A00D0F966 /* MediaPlayer */,
|
||||||
|
|
@ -458,6 +463,7 @@
|
||||||
133D7C902D2BE2640075467E /* SettingsView.swift in Sources */,
|
133D7C902D2BE2640075467E /* SettingsView.swift in Sources */,
|
||||||
13CBEFDA2D5F7D1200D011EE /* String.swift in Sources */,
|
13CBEFDA2D5F7D1200D011EE /* String.swift in Sources */,
|
||||||
130C6BFA2D53AB1F00DC1432 /* SettingsViewData.swift in Sources */,
|
130C6BFA2D53AB1F00DC1432 /* SettingsViewData.swift in Sources */,
|
||||||
|
730C73562D726DAB001EC57C /* Analytics.swift in Sources */,
|
||||||
13EA2BD72D32D97400C1EBD7 /* MusicProgressSlider.swift in Sources */,
|
13EA2BD72D32D97400C1EBD7 /* MusicProgressSlider.swift in Sources */,
|
||||||
1E9FF1D32D403E49008AC100 /* SettingsViewLoggerFilter.swift in Sources */,
|
1E9FF1D32D403E49008AC100 /* SettingsViewLoggerFilter.swift in Sources */,
|
||||||
13EA2BD92D32D98400C1EBD7 /* NormalPlayer.swift in Sources */,
|
13EA2BD92D32D98400C1EBD7 /* NormalPlayer.swift in Sources */,
|
||||||
|
|
@ -475,6 +481,7 @@
|
||||||
13103E8E2D58E04A000F0673 /* SkeletonCell.swift in Sources */,
|
13103E8E2D58E04A000F0673 /* SkeletonCell.swift in Sources */,
|
||||||
133D7C8D2D2BE2640075467E /* HomeView.swift in Sources */,
|
133D7C8D2D2BE2640075467E /* HomeView.swift in Sources */,
|
||||||
13D842552D45267500EBBFA6 /* DropManager.swift in Sources */,
|
13D842552D45267500EBBFA6 /* DropManager.swift in Sources */,
|
||||||
|
7374EF382D74AEF400319CEC /* UIDevice+Model.swift in Sources */,
|
||||||
13103E8B2D58E028000F0673 /* View.swift in Sources */,
|
13103E8B2D58E028000F0673 /* View.swift in Sources */,
|
||||||
138AA1B82D2D66FD0021F9DF /* EpisodeCell.swift in Sources */,
|
138AA1B82D2D66FD0021F9DF /* EpisodeCell.swift in Sources */,
|
||||||
133D7C8C2D2BE2640075467E /* SearchView.swift in Sources */,
|
133D7C8C2D2BE2640075467E /* SearchView.swift in Sources */,
|
||||||
|
|
|
||||||
125
Sora/Utils/Analytics/Analytics.swift
Normal file
125
Sora/Utils/Analytics/Analytics.swift
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
//
|
||||||
|
// Analytics.swift
|
||||||
|
// Sora
|
||||||
|
//
|
||||||
|
// Created by Hamzo on 28.02.25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
// MARK: - Analytics Response Model
|
||||||
|
struct AnalyticsResponse: Codable {
|
||||||
|
let status: String
|
||||||
|
let message: String
|
||||||
|
let event: String?
|
||||||
|
let timestamp: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Analytics Manager
|
||||||
|
class AnalyticsManager {
|
||||||
|
|
||||||
|
static let shared = AnalyticsManager()
|
||||||
|
private let analyticsURL = URL(string: "http://151.106.3.14:47474/analytics")!
|
||||||
|
private let moduleManager = ModuleManager()
|
||||||
|
|
||||||
|
private init() {}
|
||||||
|
|
||||||
|
// MARK: - Send Analytics Data
|
||||||
|
func sendEvent(event: String, additionalData: [String: Any] = [:]) {
|
||||||
|
|
||||||
|
let defaults = UserDefaults.standard
|
||||||
|
|
||||||
|
// Ensure the key is set with a default value if missing
|
||||||
|
if defaults.object(forKey: "analyticsEnabled") == nil {
|
||||||
|
print("Setting default value for analyticsEnabled")
|
||||||
|
defaults.setValue(true, forKey: "analyticsEnabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let analyticsEnabled = UserDefaults.standard.bool(forKey: "analyticsEnabled")
|
||||||
|
|
||||||
|
guard analyticsEnabled else {
|
||||||
|
Logger.shared.log("Analytics is disabled, skipping event: \(event)", type: "Debug")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let selectedModule = getSelectedModule() else {
|
||||||
|
Logger.shared.log("No selected module found", type: "Debug")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare analytics data
|
||||||
|
var safeAdditionalData = additionalData
|
||||||
|
|
||||||
|
// Check and convert NSError if present
|
||||||
|
if let errorValue = additionalData["error"] as? NSError {
|
||||||
|
safeAdditionalData["error"] = errorValue.localizedDescription
|
||||||
|
}
|
||||||
|
|
||||||
|
let analyticsData: [String: Any] = [
|
||||||
|
"event": event,
|
||||||
|
"device": getDeviceModel(),
|
||||||
|
"app_version": getAppVersion(),
|
||||||
|
"module_name": selectedModule.metadata.sourceName,
|
||||||
|
"module_version": selectedModule.metadata.version,
|
||||||
|
"data": safeAdditionalData
|
||||||
|
]
|
||||||
|
|
||||||
|
sendRequest(with: analyticsData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private Request Method
|
||||||
|
private func sendRequest(with data: [String: Any]) {
|
||||||
|
var request = URLRequest(url: analyticsURL)
|
||||||
|
request.httpMethod = "POST"
|
||||||
|
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||||
|
|
||||||
|
do {
|
||||||
|
request.httpBody = try JSONSerialization.data(withJSONObject: data, options: [])
|
||||||
|
} catch {
|
||||||
|
Logger.shared.log("Failed to encode JSON: \(error.localizedDescription)", type: "Debug")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
URLSession.shared.dataTask(with: request) { (data, response, error) in
|
||||||
|
if let error = error {
|
||||||
|
Logger.shared.log("Request failed: \(error.localizedDescription)", type: "Debug")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let data = data else {
|
||||||
|
Logger.shared.log("No data received from server", type: "Debug")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let decodedResponse = try JSONDecoder().decode(AnalyticsResponse.self, from: data)
|
||||||
|
if decodedResponse.status == "success" {
|
||||||
|
Logger.shared.log("Analytics saved: \(decodedResponse.event ?? "unknown event") at \(decodedResponse.timestamp ?? "unknown time")", type: "Debug")
|
||||||
|
} else {
|
||||||
|
Logger.shared.log("Server error: \(decodedResponse.message)", type: "Debug")
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Logger.shared.log("Failed to decode response: \(error.localizedDescription)", type: "Debug")
|
||||||
|
}
|
||||||
|
}.resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Get App Version
|
||||||
|
private func getAppVersion() -> String {
|
||||||
|
return Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "unknown_version"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Get Device Model
|
||||||
|
private func getDeviceModel() -> String {
|
||||||
|
return UIDevice.modelName
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Get Selected Module
|
||||||
|
private func getSelectedModule() -> ScrapingModule? {
|
||||||
|
guard let selectedModuleId = UserDefaults.standard.string(forKey: "selectedModuleId") else { return nil }
|
||||||
|
return moduleManager.modules.first { $0.id.uuidString == selectedModuleId }
|
||||||
|
}
|
||||||
|
}
|
||||||
132
Sora/Utils/Analytics/UIDevice+Model.swift
Normal file
132
Sora/Utils/Analytics/UIDevice+Model.swift
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
//
|
||||||
|
// UIDevice+Model.swift
|
||||||
|
// Sora
|
||||||
|
//
|
||||||
|
// Created by Hamzo on 02.03.25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
public extension UIDevice {
|
||||||
|
|
||||||
|
static let modelName: String = {
|
||||||
|
var systemInfo = utsname()
|
||||||
|
uname(&systemInfo)
|
||||||
|
let machineMirror = Mirror(reflecting: systemInfo.machine)
|
||||||
|
let identifier = machineMirror.children.reduce("") { identifier, element in
|
||||||
|
guard let value = element.value as? Int8, value != 0 else { return identifier }
|
||||||
|
return identifier + String(UnicodeScalar(UInt8(value)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapToDevice(identifier: String) -> String { // swiftlint:disable:this cyclomatic_complexity
|
||||||
|
#if os(iOS)
|
||||||
|
switch identifier {
|
||||||
|
case "iPod5,1": return "iPod touch (5th generation)"
|
||||||
|
case "iPod7,1": return "iPod touch (6th generation)"
|
||||||
|
case "iPod9,1": return "iPod touch (7th generation)"
|
||||||
|
case "iPhone3,1", "iPhone3,2", "iPhone3,3": return "iPhone 4"
|
||||||
|
case "iPhone4,1": return "iPhone 4s"
|
||||||
|
case "iPhone5,1", "iPhone5,2": return "iPhone 5"
|
||||||
|
case "iPhone5,3", "iPhone5,4": return "iPhone 5c"
|
||||||
|
case "iPhone6,1", "iPhone6,2": return "iPhone 5s"
|
||||||
|
case "iPhone7,2": return "iPhone 6"
|
||||||
|
case "iPhone7,1": return "iPhone 6 Plus"
|
||||||
|
case "iPhone8,1": return "iPhone 6s"
|
||||||
|
case "iPhone8,2": return "iPhone 6s Plus"
|
||||||
|
case "iPhone9,1", "iPhone9,3": return "iPhone 7"
|
||||||
|
case "iPhone9,2", "iPhone9,4": return "iPhone 7 Plus"
|
||||||
|
case "iPhone10,1", "iPhone10,4": return "iPhone 8"
|
||||||
|
case "iPhone10,2", "iPhone10,5": return "iPhone 8 Plus"
|
||||||
|
case "iPhone10,3", "iPhone10,6": return "iPhone X"
|
||||||
|
case "iPhone11,2": return "iPhone XS"
|
||||||
|
case "iPhone11,4", "iPhone11,6": return "iPhone XS Max"
|
||||||
|
case "iPhone11,8": return "iPhone XR"
|
||||||
|
case "iPhone12,1": return "iPhone 11"
|
||||||
|
case "iPhone12,3": return "iPhone 11 Pro"
|
||||||
|
case "iPhone12,5": return "iPhone 11 Pro Max"
|
||||||
|
case "iPhone13,1": return "iPhone 12 mini"
|
||||||
|
case "iPhone13,2": return "iPhone 12"
|
||||||
|
case "iPhone13,3": return "iPhone 12 Pro"
|
||||||
|
case "iPhone13,4": return "iPhone 12 Pro Max"
|
||||||
|
case "iPhone14,4": return "iPhone 13 mini"
|
||||||
|
case "iPhone14,5": return "iPhone 13"
|
||||||
|
case "iPhone14,2": return "iPhone 13 Pro"
|
||||||
|
case "iPhone14,3": return "iPhone 13 Pro Max"
|
||||||
|
case "iPhone14,7": return "iPhone 14"
|
||||||
|
case "iPhone14,8": return "iPhone 14 Plus"
|
||||||
|
case "iPhone15,2": return "iPhone 14 Pro"
|
||||||
|
case "iPhone15,3": return "iPhone 14 Pro Max"
|
||||||
|
case "iPhone15,4": return "iPhone 15"
|
||||||
|
case "iPhone15,5": return "iPhone 15 Plus"
|
||||||
|
case "iPhone16,1": return "iPhone 15 Pro"
|
||||||
|
case "iPhone16,2": return "iPhone 15 Pro Max"
|
||||||
|
case "iPhone17,3": return "iPhone 16"
|
||||||
|
case "iPhone17,4": return "iPhone 16 Plus"
|
||||||
|
case "iPhone17,1": return "iPhone 16 Pro"
|
||||||
|
case "iPhone17,2": return "iPhone 16 Pro Max"
|
||||||
|
case "iPhone8,4": return "iPhone SE"
|
||||||
|
case "iPhone12,8": return "iPhone SE (2nd generation)"
|
||||||
|
case "iPhone14,6": return "iPhone SE (3rd generation)"
|
||||||
|
case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4": return "iPad 2"
|
||||||
|
case "iPad3,1", "iPad3,2", "iPad3,3": return "iPad (3rd generation)"
|
||||||
|
case "iPad3,4", "iPad3,5", "iPad3,6": return "iPad (4th generation)"
|
||||||
|
case "iPad6,11", "iPad6,12": return "iPad (5th generation)"
|
||||||
|
case "iPad7,5", "iPad7,6": return "iPad (6th generation)"
|
||||||
|
case "iPad7,11", "iPad7,12": return "iPad (7th generation)"
|
||||||
|
case "iPad11,6", "iPad11,7": return "iPad (8th generation)"
|
||||||
|
case "iPad12,1", "iPad12,2": return "iPad (9th generation)"
|
||||||
|
case "iPad13,18", "iPad13,19": return "iPad (10th generation)"
|
||||||
|
case "iPad4,1", "iPad4,2", "iPad4,3": return "iPad Air"
|
||||||
|
case "iPad5,3", "iPad5,4": return "iPad Air 2"
|
||||||
|
case "iPad11,3", "iPad11,4": return "iPad Air (3rd generation)"
|
||||||
|
case "iPad13,1", "iPad13,2": return "iPad Air (4th generation)"
|
||||||
|
case "iPad13,16", "iPad13,17": return "iPad Air (5th generation)"
|
||||||
|
case "iPad14,8", "iPad14,9": return "iPad Air (11-inch) (M2)"
|
||||||
|
case "iPad14,10", "iPad14,11": return "iPad Air (13-inch) (M2)"
|
||||||
|
case "iPad2,5", "iPad2,6", "iPad2,7": return "iPad mini"
|
||||||
|
case "iPad4,4", "iPad4,5", "iPad4,6": return "iPad mini 2"
|
||||||
|
case "iPad4,7", "iPad4,8", "iPad4,9": return "iPad mini 3"
|
||||||
|
case "iPad5,1", "iPad5,2": return "iPad mini 4"
|
||||||
|
case "iPad11,1", "iPad11,2": return "iPad mini (5th generation)"
|
||||||
|
case "iPad14,1", "iPad14,2": return "iPad mini (6th generation)"
|
||||||
|
case "iPad16,1", "iPad16,2": return "iPad mini (A17 Pro)"
|
||||||
|
case "iPad6,3", "iPad6,4": return "iPad Pro (9.7-inch)"
|
||||||
|
case "iPad7,3", "iPad7,4": return "iPad Pro (10.5-inch)"
|
||||||
|
case "iPad8,1", "iPad8,2", "iPad8,3", "iPad8,4": return "iPad Pro (11-inch) (1st generation)"
|
||||||
|
case "iPad8,9", "iPad8,10": return "iPad Pro (11-inch) (2nd generation)"
|
||||||
|
case "iPad13,4", "iPad13,5", "iPad13,6", "iPad13,7": return "iPad Pro (11-inch) (3rd generation)"
|
||||||
|
case "iPad14,3", "iPad14,4": return "iPad Pro (11-inch) (4th generation)"
|
||||||
|
case "iPad16,3", "iPad16,4": return "iPad Pro (11-inch) (M4)"
|
||||||
|
case "iPad6,7", "iPad6,8": return "iPad Pro (12.9-inch) (1st generation)"
|
||||||
|
case "iPad7,1", "iPad7,2": return "iPad Pro (12.9-inch) (2nd generation)"
|
||||||
|
case "iPad8,5", "iPad8,6", "iPad8,7", "iPad8,8": return "iPad Pro (12.9-inch) (3rd generation)"
|
||||||
|
case "iPad8,11", "iPad8,12": return "iPad Pro (12.9-inch) (4th generation)"
|
||||||
|
case "iPad13,8", "iPad13,9", "iPad13,10", "iPad13,11":return "iPad Pro (12.9-inch) (5th generation)"
|
||||||
|
case "iPad14,5", "iPad14,6": return "iPad Pro (12.9-inch) (6th generation)"
|
||||||
|
case "iPad16,5", "iPad16,6": return "iPad Pro (13-inch) (M4)"
|
||||||
|
case "AppleTV5,3": return "Apple TV"
|
||||||
|
case "AppleTV6,2": return "Apple TV 4K"
|
||||||
|
case "AudioAccessory1,1": return "HomePod"
|
||||||
|
case "AudioAccessory5,1": return "HomePod mini"
|
||||||
|
case "i386", "x86_64", "arm64": return "Simulator \(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "iOS"))"
|
||||||
|
default: return identifier
|
||||||
|
}
|
||||||
|
#elseif os(tvOS)
|
||||||
|
switch identifier {
|
||||||
|
case "AppleTV5,3": return "Apple TV 4"
|
||||||
|
case "AppleTV6,2", "AppleTV11,1", "AppleTV14,1": return "Apple TV 4K"
|
||||||
|
case "i386", "x86_64": return "Simulator \(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "tvOS"))"
|
||||||
|
default: return identifier
|
||||||
|
}
|
||||||
|
#elseif os(visionOS)
|
||||||
|
switch identifier {
|
||||||
|
case "RealityDevice14,1": return "Apple Vision Pro"
|
||||||
|
default: return identifier
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapToDevice(identifier: identifier)
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -31,6 +31,13 @@ class Logger {
|
||||||
let entry = LogEntry(message: message, type: type, timestamp: Date())
|
let entry = LogEntry(message: message, type: type, timestamp: Date())
|
||||||
logs.append(entry)
|
logs.append(entry)
|
||||||
saveLogToFile(entry)
|
saveLogToFile(entry)
|
||||||
|
|
||||||
|
// Print to Xcode console
|
||||||
|
let dateFormatter = DateFormatter()
|
||||||
|
dateFormatter.dateFormat = "dd-MM HH:mm:ss"
|
||||||
|
let formattedMessage = "[\(dateFormatter.string(from: entry.timestamp))] [\(entry.type)] \(entry.message)"
|
||||||
|
|
||||||
|
//print(formattedMessage) // TODO: Remove this line in production, DEBUG only
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLogs() -> String {
|
func getLogs() -> String {
|
||||||
|
|
|
||||||
|
|
@ -214,6 +214,7 @@ struct MediaInfoView: View {
|
||||||
selectedEpisodeNumber = ep.number
|
selectedEpisodeNumber = ep.number
|
||||||
selectedEpisodeImage = imageUrl
|
selectedEpisodeImage = imageUrl
|
||||||
fetchStream(href: ep.href)
|
fetchStream(href: ep.href)
|
||||||
|
AnalyticsManager.shared.sendEvent(event: "watch", additionalData: ["title": title, "episode": ep.number])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -268,9 +269,11 @@ struct MediaInfoView: View {
|
||||||
itemID = id
|
itemID = id
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
Logger.shared.log("Failed to fetch Item ID: \(error)")
|
Logger.shared.log("Failed to fetch Item ID: \(error)")
|
||||||
|
AnalyticsManager.shared.sendEvent(event: "error", additionalData: ["error": error, "message": "Failed to fetch Item ID"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hasFetched = true
|
hasFetched = true
|
||||||
|
AnalyticsManager.shared.sendEvent(event: "search", additionalData: ["title": title])
|
||||||
}
|
}
|
||||||
selectedRange = 0..<episodeChunkSize
|
selectedRange = 0..<episodeChunkSize
|
||||||
}
|
}
|
||||||
|
|
@ -476,6 +479,7 @@ struct MediaInfoView: View {
|
||||||
func handleStreamFailure(error: Error? = nil) {
|
func handleStreamFailure(error: Error? = nil) {
|
||||||
if let error = error {
|
if let error = error {
|
||||||
Logger.shared.log("Error loading module: \(error)", type: "Error")
|
Logger.shared.log("Error loading module: \(error)", type: "Error")
|
||||||
|
AnalyticsManager.shared.sendEvent(event: "error", additionalData: ["error": error, "message": "Failed to fetch stream"])
|
||||||
}
|
}
|
||||||
DropManager.shared.showDrop(title: "Stream not Found", subtitle: "", duration: 1.0, icon: UIImage(systemName: "xmark"))
|
DropManager.shared.showDrop(title: "Stream not Found", subtitle: "", duration: 1.0, icon: UIImage(systemName: "xmark"))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ struct SettingsViewGeneral: View {
|
||||||
@AppStorage("episodeChunkSize") private var episodeChunkSize: Int = 100
|
@AppStorage("episodeChunkSize") private var episodeChunkSize: Int = 100
|
||||||
@AppStorage("refreshModulesOnLaunch") private var refreshModulesOnLaunch: Bool = false
|
@AppStorage("refreshModulesOnLaunch") private var refreshModulesOnLaunch: Bool = false
|
||||||
@AppStorage("fetchEpisodeMetadata") private var fetchEpisodeMetadata: Bool = true
|
@AppStorage("fetchEpisodeMetadata") private var fetchEpisodeMetadata: Bool = true
|
||||||
|
@AppStorage("analyticsEnabled") private var analyticsEnabled: Bool = true
|
||||||
@EnvironmentObject var settings: Settings
|
@EnvironmentObject var settings: Settings
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
|
@ -57,6 +58,10 @@ struct SettingsViewGeneral: View {
|
||||||
Toggle("Refresh Modules on Launch", isOn: $refreshModulesOnLaunch)
|
Toggle("Refresh Modules on Launch", isOn: $refreshModulesOnLaunch)
|
||||||
.tint(.accentColor)
|
.tint(.accentColor)
|
||||||
}
|
}
|
||||||
|
Section(header: Text("Analytics"), footer: Text("Allow Sora to collect anonymous data to improve the app. No personal information is collected. This can be disabled at any time.\n\n Information collected: \n- App version\n- Device model\n- Module Name/Version\n- Error Messages\n- Title of Watched Content")) {
|
||||||
|
Toggle("Enable Analytics", isOn: $analyticsEnabled)
|
||||||
|
.tint(.accentColor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("General")
|
.navigationTitle("General")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue