2020-02-16 18:47:09 +00:00
|
|
|
//
|
|
|
|
// Playlist.swift
|
2022-08-07 13:31:15 +01:00
|
|
|
// Mixonomer
|
2020-02-16 18:47:09 +00:00
|
|
|
//
|
|
|
|
// Created by Andy Pack on 16/02/2020.
|
|
|
|
// Copyright © 2020 Sarsoo. All rights reserved.
|
|
|
|
//
|
|
|
|
|
2020-03-06 23:36:51 +00:00
|
|
|
import Foundation
|
2020-02-16 18:47:09 +00:00
|
|
|
import UIKit
|
2020-02-18 21:43:30 +00:00
|
|
|
import SwiftyJSON
|
2022-11-23 23:39:58 +00:00
|
|
|
import OSLog
|
2020-02-17 02:21:49 +00:00
|
|
|
|
2020-04-30 17:05:23 +01:00
|
|
|
class Playlist: Identifiable, Equatable, Codable, ObservableObject {
|
2020-02-16 18:47:09 +00:00
|
|
|
|
|
|
|
//MARK: Properties
|
|
|
|
|
2020-04-30 17:05:23 +01:00
|
|
|
@Published var name: String
|
|
|
|
@Published var uri: String
|
|
|
|
@Published var username: String?
|
2020-02-16 18:47:09 +00:00
|
|
|
|
2020-04-30 17:05:23 +01:00
|
|
|
@Published var type: String {
|
|
|
|
didSet {
|
|
|
|
self.updatePlaylist(updates: JSON(["type": self.type]))
|
|
|
|
}
|
|
|
|
}
|
2020-02-16 18:47:09 +00:00
|
|
|
|
2020-04-30 17:05:23 +01:00
|
|
|
@Published var include_recommendations: Bool {
|
|
|
|
didSet {
|
|
|
|
self.updatePlaylist(updates: JSON(["include_recommendations": self.include_recommendations]))
|
|
|
|
}
|
|
|
|
}
|
2022-12-09 08:51:42 +00:00
|
|
|
@Published var recommendation_sample: Int {
|
2020-04-30 17:05:23 +01:00
|
|
|
didSet {
|
|
|
|
self.updatePlaylist(updates: JSON(["recommendation_sample": self.recommendation_sample]))
|
|
|
|
}
|
|
|
|
}
|
2022-12-09 08:51:42 +00:00
|
|
|
@Published var include_library_tracks: Bool {
|
2020-04-30 17:05:23 +01:00
|
|
|
didSet {
|
|
|
|
self.updatePlaylist(updates: JSON(["include_library_tracks": self.include_library_tracks]))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-09 08:51:42 +00:00
|
|
|
@Published var parts: Array<String> {
|
2020-04-30 17:05:23 +01:00
|
|
|
didSet {
|
|
|
|
self.updatePlaylist(updates: JSON(["parts": self.parts]))
|
|
|
|
}
|
|
|
|
}
|
2022-12-09 08:51:42 +00:00
|
|
|
@Published var playlist_references: Array<String> {
|
2020-04-30 17:05:23 +01:00
|
|
|
didSet {
|
|
|
|
self.updatePlaylist(updates: JSON(["playlist_references": self.playlist_references]))
|
|
|
|
}
|
|
|
|
}
|
2022-12-09 08:51:42 +00:00
|
|
|
@Published var shuffle: Bool {
|
2020-04-30 17:05:23 +01:00
|
|
|
didSet {
|
|
|
|
self.updatePlaylist(updates: JSON(["shuffle": self.shuffle]))
|
|
|
|
}
|
|
|
|
}
|
2020-02-16 18:47:09 +00:00
|
|
|
|
2020-04-30 17:05:23 +01:00
|
|
|
var sort: String?
|
|
|
|
@Published var description_overwrite: String?
|
|
|
|
@Published var description_suffix: String?
|
2020-03-06 23:36:51 +00:00
|
|
|
|
2020-04-30 17:05:23 +01:00
|
|
|
@Published var last_updated: String?
|
2020-03-06 23:36:51 +00:00
|
|
|
|
2020-04-30 17:05:23 +01:00
|
|
|
@Published var lastfm_stat_count: Int
|
|
|
|
@Published var lastfm_stat_album_count: Int
|
|
|
|
@Published var lastfm_stat_artist_count: Int
|
2020-03-06 23:36:51 +00:00
|
|
|
|
2020-04-30 17:05:23 +01:00
|
|
|
@Published var lastfm_stat_percent: Float
|
2020-03-07 18:51:52 +00:00
|
|
|
var lastfm_stat_percent_str: String {
|
|
|
|
get {
|
|
|
|
return String(format: "%.2f%%", lastfm_stat_percent)
|
|
|
|
}
|
|
|
|
}
|
2020-04-30 17:05:23 +01:00
|
|
|
@Published var lastfm_stat_album_percent: Float
|
2020-03-07 18:51:52 +00:00
|
|
|
var lastfm_stat_album_percent_str: String {
|
|
|
|
get {
|
|
|
|
return String(format: "%.2f%%", lastfm_stat_album_percent)
|
|
|
|
}
|
|
|
|
}
|
2020-04-30 17:05:23 +01:00
|
|
|
@Published var lastfm_stat_artist_percent: Float
|
2020-03-07 18:51:52 +00:00
|
|
|
var lastfm_stat_artist_percent_str: String {
|
|
|
|
get {
|
|
|
|
return String(format: "%.2f%%", lastfm_stat_artist_percent)
|
|
|
|
}
|
|
|
|
}
|
2020-03-06 23:36:51 +00:00
|
|
|
|
2020-04-30 17:05:23 +01:00
|
|
|
@Published var lastfm_stat_last_refresh: String?
|
|
|
|
|
2022-12-09 08:51:42 +00:00
|
|
|
@Published var add_last_month: Bool {
|
2020-04-30 17:05:23 +01:00
|
|
|
didSet {
|
|
|
|
self.updatePlaylist(updates: JSON(["add_last_month": self.add_last_month]))
|
|
|
|
}
|
|
|
|
}
|
2022-12-09 08:51:42 +00:00
|
|
|
@Published var add_this_month: Bool {
|
2020-04-30 17:05:23 +01:00
|
|
|
didSet {
|
|
|
|
self.updatePlaylist(updates: JSON(["add_this_month": self.add_this_month]))
|
|
|
|
}
|
|
|
|
}
|
2022-12-09 08:51:42 +00:00
|
|
|
@Published var day_boundary: Int {
|
2020-04-30 17:05:23 +01:00
|
|
|
didSet {
|
|
|
|
self.updatePlaylist(updates: JSON(["day_boundary": self.day_boundary]))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-09 08:51:42 +00:00
|
|
|
@Published var chart_range: LastFmRange {
|
2020-04-30 17:05:23 +01:00
|
|
|
didSet {
|
|
|
|
self.updatePlaylist(updates: JSON(["chart_range": self.chart_range.rawValue]))
|
|
|
|
}
|
|
|
|
}
|
2022-12-09 08:51:42 +00:00
|
|
|
@Published var chart_limit: Int {
|
2020-04-30 17:05:23 +01:00
|
|
|
didSet {
|
2022-12-09 08:51:42 +00:00
|
|
|
self.updatePlaylist(updates: JSON(["chart_limit": self.chart_limit]))
|
2020-04-30 17:05:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func updatePlaylist(updates: JSON) {
|
2022-12-09 08:51:42 +00:00
|
|
|
let api = PlaylistApi.updatePlaylist(name: self.name, updates: updates)
|
|
|
|
RequestBuilder.buildRequest(apiRequest: api).responseJSON{ response in
|
|
|
|
switch response.response?.statusCode {
|
|
|
|
case 200, 201:
|
|
|
|
break
|
|
|
|
case _:
|
|
|
|
Logger.net.error("error: \(self.name), \(updates)")
|
|
|
|
}
|
2020-04-30 17:05:23 +01:00
|
|
|
}
|
2022-12-09 08:51:42 +00:00
|
|
|
//TODO: do better error checking
|
2020-04-30 17:05:23 +01:00
|
|
|
}
|
2020-03-06 23:36:51 +00:00
|
|
|
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
|
|
case name
|
|
|
|
case uri
|
|
|
|
case username
|
|
|
|
|
2020-04-30 17:05:23 +01:00
|
|
|
case type
|
|
|
|
|
2020-03-06 23:36:51 +00:00
|
|
|
case include_recommendations
|
|
|
|
case recommendation_sample
|
|
|
|
case include_library_tracks
|
|
|
|
|
|
|
|
case parts
|
|
|
|
case playlist_references
|
|
|
|
case shuffle
|
|
|
|
|
|
|
|
case sort
|
|
|
|
case description_overwrite
|
|
|
|
case description_suffix
|
|
|
|
|
|
|
|
case last_updated
|
|
|
|
|
|
|
|
case lastfm_stat_count
|
|
|
|
case lastfm_stat_album_count
|
|
|
|
case lastfm_stat_artist_count
|
|
|
|
|
|
|
|
case lastfm_stat_percent
|
|
|
|
case lastfm_stat_album_percent
|
|
|
|
case lastfm_stat_artist_percent
|
|
|
|
|
|
|
|
case lastfm_stat_last_refresh
|
2020-04-30 17:05:23 +01:00
|
|
|
|
|
|
|
case add_last_month
|
|
|
|
case add_this_month
|
|
|
|
case day_boundary
|
|
|
|
|
|
|
|
case chart_range
|
|
|
|
case chart_limit
|
2020-03-06 23:36:51 +00:00
|
|
|
}
|
|
|
|
|
2020-02-16 18:47:09 +00:00
|
|
|
//MARK: Initialization
|
|
|
|
|
|
|
|
init(name: String,
|
2020-03-06 23:36:51 +00:00
|
|
|
uri: String = "spotify::",
|
|
|
|
username: String = "NO USER",
|
2020-04-30 17:05:23 +01:00
|
|
|
|
|
|
|
type: String = "default",
|
2020-03-06 23:36:51 +00:00
|
|
|
|
|
|
|
include_recommendations: Bool = false,
|
|
|
|
recommendation_sample: Int = 0,
|
|
|
|
include_library_tracks: Bool = false,
|
|
|
|
|
|
|
|
parts: Array<String> = [],
|
|
|
|
playlist_references: Array<String> = [],
|
|
|
|
shuffle: Bool = false,
|
2020-02-16 18:47:09 +00:00
|
|
|
|
2020-03-06 23:36:51 +00:00
|
|
|
sort: String = "NO SORT",
|
|
|
|
description_overwrite: String? = nil,
|
|
|
|
description_suffix: String? = nil,
|
2020-02-16 18:47:09 +00:00
|
|
|
|
2020-03-20 00:08:18 +00:00
|
|
|
last_updated: String? = "",
|
2020-02-16 18:47:09 +00:00
|
|
|
|
2020-03-06 23:36:51 +00:00
|
|
|
lastfm_stat_count: Int = 0,
|
|
|
|
lastfm_stat_album_count: Int = 0,
|
|
|
|
lastfm_stat_artist_count: Int = 0,
|
|
|
|
|
|
|
|
lastfm_stat_percent: Float = 0,
|
|
|
|
lastfm_stat_album_percent: Float = 0,
|
|
|
|
lastfm_stat_artist_percent: Float = 0,
|
|
|
|
|
2020-04-30 17:05:23 +01:00
|
|
|
lastfm_stat_last_refresh: String? = "",
|
|
|
|
|
|
|
|
add_last_month: Bool = false,
|
|
|
|
add_this_month: Bool = false,
|
|
|
|
day_boundary: Int = 14,
|
|
|
|
|
|
|
|
chart_range: LastFmRange = .overall,
|
|
|
|
chart_limit: Int = 10){
|
2020-03-06 23:36:51 +00:00
|
|
|
|
2020-02-16 18:47:09 +00:00
|
|
|
self.name = name
|
|
|
|
self.uri = uri
|
|
|
|
self.username = username
|
2020-03-06 23:36:51 +00:00
|
|
|
|
2020-04-30 17:05:23 +01:00
|
|
|
self.type = type
|
|
|
|
|
2020-03-06 23:36:51 +00:00
|
|
|
self.last_updated = last_updated
|
|
|
|
|
|
|
|
self.lastfm_stat_count = lastfm_stat_count
|
|
|
|
self.lastfm_stat_album_count = lastfm_stat_album_count
|
|
|
|
self.lastfm_stat_artist_count = lastfm_stat_artist_count
|
|
|
|
|
|
|
|
self.lastfm_stat_percent = lastfm_stat_percent
|
|
|
|
self.lastfm_stat_album_percent = lastfm_stat_album_percent
|
|
|
|
self.lastfm_stat_artist_percent = lastfm_stat_artist_percent
|
|
|
|
|
|
|
|
self.lastfm_stat_last_refresh = lastfm_stat_last_refresh
|
2020-03-20 00:08:18 +00:00
|
|
|
|
|
|
|
self.include_recommendations = include_recommendations
|
|
|
|
self.recommendation_sample = recommendation_sample
|
|
|
|
self.include_library_tracks = include_library_tracks
|
|
|
|
|
|
|
|
self.parts = parts
|
|
|
|
self.playlist_references = playlist_references
|
|
|
|
self.shuffle = shuffle
|
|
|
|
|
|
|
|
self.sort = sort
|
|
|
|
self.description_overwrite = description_overwrite
|
|
|
|
self.description_suffix = description_suffix
|
2020-04-30 17:05:23 +01:00
|
|
|
|
|
|
|
self.add_last_month = add_last_month
|
|
|
|
self.add_this_month = add_this_month
|
|
|
|
self.day_boundary = day_boundary
|
|
|
|
|
|
|
|
self.chart_range = chart_range
|
|
|
|
self.chart_limit = chart_limit
|
2020-02-18 21:43:30 +00:00
|
|
|
}
|
2020-02-16 18:47:09 +00:00
|
|
|
|
2020-02-19 23:00:23 +00:00
|
|
|
var link: String {
|
|
|
|
let uriSplit = self.uri.components(separatedBy: ":")
|
|
|
|
return "https://open.spotify.com/playlist/\(uriSplit.last ?? "")"
|
|
|
|
}
|
|
|
|
|
|
|
|
static func == (lhs: Playlist, rhs: Playlist) -> Bool {
|
|
|
|
return lhs.name == rhs.name
|
|
|
|
// && lhs.username == rhs.username
|
|
|
|
}
|
2020-03-06 23:36:51 +00:00
|
|
|
required init(from decoder: Decoder) throws {
|
|
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
|
|
|
|
|
|
name = try container.decode(String.self, forKey: .name)
|
|
|
|
uri = try container.decode(String.self, forKey: .uri)
|
2020-04-30 17:05:23 +01:00
|
|
|
do{
|
|
|
|
username = try container.decode(String.self, forKey: .username)
|
|
|
|
}catch {
|
|
|
|
username = "NO USER"
|
2022-11-23 23:39:58 +00:00
|
|
|
Logger.parse.warning("failed to parse username")
|
2020-04-30 17:05:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type = try container.decode(String.self, forKey: .type)
|
2020-03-06 23:36:51 +00:00
|
|
|
|
2020-04-30 17:05:23 +01:00
|
|
|
do{
|
|
|
|
description_overwrite = try container.decode(String.self, forKey: .description_overwrite)
|
|
|
|
}catch {
|
|
|
|
}
|
2020-03-06 23:36:51 +00:00
|
|
|
|
2020-04-30 17:05:23 +01:00
|
|
|
do{
|
|
|
|
description_suffix = try container.decode(String.self, forKey: .description_suffix)
|
|
|
|
}catch {
|
|
|
|
}
|
2020-03-06 23:36:51 +00:00
|
|
|
last_updated = try container.decode(String.self, forKey: .last_updated)
|
|
|
|
|
|
|
|
lastfm_stat_count = try container.decode(Int.self, forKey: .lastfm_stat_count)
|
|
|
|
lastfm_stat_album_count = try container.decode(Int.self, forKey: .lastfm_stat_album_count)
|
|
|
|
lastfm_stat_artist_count = try container.decode(Int.self, forKey: .lastfm_stat_artist_count)
|
|
|
|
|
|
|
|
lastfm_stat_percent = try container.decode(Float.self, forKey: .lastfm_stat_percent)
|
|
|
|
lastfm_stat_album_percent = try container.decode(Float.self, forKey: .lastfm_stat_album_percent)
|
|
|
|
lastfm_stat_artist_percent = try container.decode(Float.self, forKey: .lastfm_stat_artist_percent)
|
|
|
|
|
|
|
|
lastfm_stat_last_refresh = try container.decode(String.self, forKey: .lastfm_stat_last_refresh)
|
|
|
|
|
2020-03-20 00:08:18 +00:00
|
|
|
include_recommendations = try container.decode(Bool.self, forKey: .include_recommendations)
|
|
|
|
recommendation_sample = try container.decode(Int.self, forKey: .recommendation_sample)
|
2020-04-30 17:05:23 +01:00
|
|
|
|
|
|
|
do{
|
|
|
|
include_library_tracks = try container.decode(Bool.self, forKey: .include_library_tracks)
|
|
|
|
}catch {
|
|
|
|
include_library_tracks = false
|
|
|
|
}
|
2020-03-20 00:08:18 +00:00
|
|
|
|
|
|
|
parts = try container.decode([String].self, forKey: .parts)
|
|
|
|
playlist_references = try container.decode([String].self, forKey: .playlist_references)
|
|
|
|
shuffle = try container.decode(Bool.self, forKey: .shuffle)
|
|
|
|
|
2020-04-30 17:05:23 +01:00
|
|
|
do{
|
|
|
|
sort = try container.decode(String.self, forKey: .sort)
|
|
|
|
}catch {
|
|
|
|
sort = "release_date"
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
add_last_month = try container.decode(Bool.self, forKey: .add_last_month)
|
|
|
|
}catch {
|
|
|
|
add_last_month = false
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
add_this_month = try container.decode(Bool.self, forKey: .add_this_month)
|
|
|
|
}catch {
|
|
|
|
add_this_month = false
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
day_boundary = try container.decode(Int.self, forKey: .day_boundary)
|
|
|
|
}catch {
|
|
|
|
day_boundary = 21
|
|
|
|
}
|
2020-02-23 00:24:02 +00:00
|
|
|
|
2020-04-30 17:05:23 +01:00
|
|
|
do{
|
|
|
|
chart_range = try LastFmRange(rawValue: container.decode(String.self, forKey: .chart_range)) ?? LastFmRange.month
|
|
|
|
}catch {
|
|
|
|
chart_range = .halfyear
|
|
|
|
}
|
|
|
|
|
|
|
|
do{
|
|
|
|
chart_limit = try container.decode(Int.self, forKey: .chart_limit)
|
|
|
|
}catch {
|
|
|
|
chart_limit = 50
|
|
|
|
}
|
2020-03-06 23:36:51 +00:00
|
|
|
}
|
|
|
|
|
2020-04-30 17:05:23 +01:00
|
|
|
func encode(to encoder: Encoder) throws {
|
|
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
|
|
|
|
|
|
try container.encode(self.name, forKey: .name)
|
|
|
|
try container.encode(self.uri, forKey: .uri)
|
|
|
|
try container.encode(self.username, forKey: .username)
|
|
|
|
|
|
|
|
try container.encode(self.type, forKey: .type)
|
|
|
|
|
|
|
|
try container.encode(self.include_recommendations, forKey: .include_recommendations)
|
|
|
|
try container.encode(self.recommendation_sample, forKey: .recommendation_sample)
|
|
|
|
try container.encode(self.include_library_tracks, forKey: .include_library_tracks)
|
|
|
|
|
|
|
|
try container.encode(self.parts, forKey: .parts)
|
|
|
|
try container.encode(self.playlist_references, forKey: .playlist_references)
|
|
|
|
try container.encode(self.shuffle, forKey: .shuffle)
|
|
|
|
|
|
|
|
try container.encode(self.sort, forKey: .sort)
|
|
|
|
try container.encode(self.description_overwrite, forKey: .description_overwrite)
|
|
|
|
try container.encode(self.description_suffix, forKey: .description_suffix)
|
|
|
|
|
|
|
|
try container.encode(self.last_updated, forKey: .last_updated)
|
2020-03-06 23:36:51 +00:00
|
|
|
|
2020-04-30 17:05:23 +01:00
|
|
|
try container.encode(self.lastfm_stat_count, forKey: .lastfm_stat_count)
|
|
|
|
try container.encode(self.lastfm_stat_album_count, forKey: .lastfm_stat_album_count)
|
|
|
|
try container.encode(self.lastfm_stat_artist_count, forKey: .lastfm_stat_artist_count)
|
2020-03-06 23:36:51 +00:00
|
|
|
|
2020-04-30 17:05:23 +01:00
|
|
|
try container.encode(self.lastfm_stat_percent, forKey: .lastfm_stat_percent)
|
|
|
|
try container.encode(self.lastfm_stat_album_percent, forKey: .lastfm_stat_album_percent)
|
|
|
|
try container.encode(self.lastfm_stat_artist_percent, forKey: .lastfm_stat_artist_percent)
|
|
|
|
|
|
|
|
try container.encode(self.lastfm_stat_last_refresh, forKey: .lastfm_stat_last_refresh)
|
|
|
|
|
|
|
|
try container.encode(self.add_last_month, forKey: .add_last_month)
|
|
|
|
try container.encode(self.add_this_month, forKey: .add_this_month)
|
|
|
|
try container.encode(self.day_boundary, forKey: .day_boundary)
|
|
|
|
|
|
|
|
try container.encode(self.chart_range, forKey: .chart_range)
|
|
|
|
try container.encode(self.chart_limit, forKey: .chart_limit)
|
2020-02-23 00:24:02 +00:00
|
|
|
}
|
2020-04-30 17:05:23 +01:00
|
|
|
|
2020-02-23 00:24:02 +00:00
|
|
|
}
|
|
|
|
|
2020-04-30 17:05:23 +01:00
|
|
|
enum LastFmRange: String, Codable {
|
2020-02-23 00:24:02 +00:00
|
|
|
case overall = "OVERALL"
|
|
|
|
case week = "WEEK"
|
|
|
|
case month = "MONTH"
|
|
|
|
case quarter = "QUARTER"
|
|
|
|
case halfyear = "HALFYEAR"
|
|
|
|
case year = "YEAR"
|
|
|
|
}
|