2 posts were split to a new topic: Spotify Playlist
Here is my version, @realliyifei
This week’s upcoming events with date info on top. The date abbreviation is local.
(Shortcoming for some is that it only displays current week events only. I have to fix it so you can choose when next week starts to show.)
Edit: Cleaned up comments and variable names.
// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: red; icon-glyph: calendar-alt;
// This widget is based on code by Max Zeryck @mzeryck
// GitHub: https://gist.github.com/mzeryck/4f9255224fe707ee74d86dc6465feea2
// Scriptable Forum post: https://talk.automators.fm/t/widget-examples/7994/83
//
// Alterations and additional commenting made by Erkka
// The widget shows
// - date, weekday and week number on top
// - next event time and title
// - upcoming event titles
// - this should work in all widget sizes
// - note: the widget shows only current week
// TEST MODE SELECTOR
// If true, tapping the script/widget
// shows a pop-up with refreshed widget
// If false, it the calendar launches
const TEST_MODE = true
// ------------------------
// User tweakable constants
// Font sizes
const headerFont = 20
const firstEvent = 14
const listEvents = 10
// Space before and after the 1st event
const firstPadding = 8
// Space between other events
const eventSpacer = 6
// Max number of events shown
const maxEvents = 7
// Date formatting:
// Check Scriptable documentation: "DateFormat"
// Used here:
// E => short weekday, like "mon"
// dd => day 01-31
// MM => month 01-12
// ww => week number
// Note: You can use additional characters
// and spaces... "(yyyy)" => (2020)
// Header date
const headerDateFormat = "dd/MM E/ww"
// Prefix in event listing
const eventPrefix = "dd/ "
// ---------------
// Other constants
// Let's store the current datetime
const date = new Date()
// -----------------------------
// If the widget is running (and test mode is false)
// tapping the widget (or preas "play" here in the Scriptable editor) the Calendar opens...
if (!config.runsInWidget && !TEST_MODE) {
// I'm not sure what is the logic here,
// but it works
const appleDate = new Date('2001/01/01')
const timestamp = (date.getTime() - appleDate.getTime()) / 1000
const callback = new CallbackURL("calshow:"+timestamp)
callback.open()
Script.complete()
// ...otherwise, create the widget.
} else {
// ---------------------------------
// FIRST WE CREATE THE EVENT ARRAY
// Get calendar events into an array
// scope used: thisWeek
const events = await CalendarEvent.thisWeek([])
// Setup an array
let futureEvents = []
// Put all future events in the array
for (const event of events) {
if (event.startDate.getTime() > date.getTime()){
futureEvents.push(event)
}
}
// Count the events in the array
// We'll use this later
let eventsTotal = futureEvents.length
// ARRAY STUFF IS NOW READY
// ----------------------------
// NEXT: Let's build the widget
// -----------------
// THE WIDGET BEGINS
let widget = new ListWidget()
// FIRST: THE HEADER
// (The top row with current date)
// A new date formatter for the hader date.
// Format set in a const at the beginning.
let dfA = new DateFormatter()
dfA.dateFormat = headerDateFormat
// Print the date header: Take formatted date,
// make it an uppercased string
let headerText = widget.addText(dfA.string(date).toUpperCase())
// ...and now we can STYLIZE it.
// Check Scriptable documentation ("Font")
// to find a list of available fonts
// Font size can be tweaked at the beginning
headerText.font = Font.mediumSystemFont(headerFont)
headerText.textColor = Color.red()
// Store today's date so we can compare it later
dfA.dateFormat = "ddMMyyyy"
let todayIs = dfA.string(date)
// HEADER STUFF IS NOW DONE
// -------------------------
// NEXT: THE EVENT LIST
// We'll need another date formatter,
// This will be used in all events
let dfB = new DateFormatter()
// Store 1st event's date so we can compare it later
dfB.dateFormat = "ddMMyyyy"
let firstIs = dfB.string(futureEvents[0].startDate)
dfB.dateFormat = eventPrefix
// CREATE: 1st EVENT
// First we have to check:
// do we even have any events...
if (eventsTotal < 1) {
// If there are no events,
// we add a spacer and call it a day.
// You could print something nice here :)
widget.addSpacer()
// But if there are events...
} else {
// ...let's define how the 1st entry looks.
// It is a special one and uses 2 lines.
// First some padding. Tweak the value
// at the beginning (const firstPadding)
widget.addSpacer(firstPadding)
// Store 1st event's day and time.
// If the first event is today,
// we won't use a date prefix.
let firstDate
if(todayIs === firstIs){
firstDate = ''
} else {
// Event day number
firstDate = dfB.string(futureEvents[0].startDate) + ' '
}
// Event time span values
let firstStarts = formatTime(futureEvents[0].startDate)
let firstEnds = formatTime(futureEvents[0].endDate)
// PRINT 1st line: 1st event's (date and) time
let firstLine = widget.addText(firstDate + firstStarts + '-' + firstEnds)
// Add some space vetween the lines
widget.addSpacer(5)
// PRINT 2nd line: 1st event's title
let secondLine = widget.addText(futureEvents[0].title)
// STYLIZE: 1st/2nd line font & color
// Font size const (firstEvent)
// was defined at the beginning
firstLine.font = Font.lightSystemFont(firstEvent)
firstLine.textColor = Color.white()
secondLine.font = Font.mediumSystemFont(firstEvent)
secondLine.textColor = Color.red()
// If there's more than 3 events, limit the title
// lines to 1, else limit to 2
if(eventsTotal > 3){ secondLine.lineLimit = 1} else {secondLine.lineLimit = 2}
// Add same amount of padding as we
// did before the first entry
widget.addSpacer(firstPadding)
// FIRST EVENT IS NOW DONE
// -----------------------
// NEXT: EVENTS 2-7 etc.
// Again: We have to check if we have more events.
// If there was only 1...
if (eventsTotal < 2) {
// ...if so, we a spacer...
widget.addSpacer()
// ...otherwise we go on!
} else {
// Now we check how many events we're
// in total (we already printed one)
// Setup a variable
var eventsToList
// We defined the max const at the beginning.
// Now compare that to the event array.
// If we have less than max, value is that
// If we have same as max or more, value is max
if (eventsTotal < maxEvents){eventsToList = eventsTotal} else {eventsToList = maxEvents}
// PRINT the remaining events
var i
// We have already peinted 1 event
// so array index i is set as 1
for(i = 1; i < eventsToList; i++){
// Store day number and title
let eventDateprefix = dfB.string(futureEvents[i].startDate)
let eventTitle = futureEvents[i].title
// Print day + title
let listEvent = widget.addText(eventDateprefix + eventTitle)
// STYLIZE: event line font & color
// Font size const (listEvent)
// was set at the beginning
listEvent.font = Font.regularSystemFont(listEvents)
listEvent.textColor = Color.white()
// Limit lines to 1 per event
listEvent.lineLimit = 1
// Add spacer, const was set at the beginning
widget.addSpacer(eventSpacer)
// For-loop ends here!
}
// The "else" after
// if-we-have-more-than-1-event
// ends here:
}
// ---------------------------------
// The "else" after
// if-we-have-more-than-0-events
// ends here:
}
// ---------------------------------
// A spacer to even things out
widget.addSpacer()
// ------------------------
// Finalize widget settings
widget.setPadding(15,15,15,0)
widget.spacing = -3
Script.setWidget(widget)
widget.presentSmall()
Script.complete()
}
// BOOM. DONE.
// ---------------------------
// FUNCTIONS ARE DEFINED BELOW
// ----------------------------------
// Function: formatTime
// TIME FORMATTER
// Is used to format the event times
function formatTime(date) {
let df = new DateFormatter()
df.useNoDateStyle()
df.useShortTimeStyle()
return df.string(date)
}
// ----------------------------------
Similar to my next game widget (using an API): next concert widget (using my calendar)
(Don’t get jealous fellow U2 fans; fake data…)
And continuing the use of my calendar, but adding the new stacks: displaying the magazines that appear in the next week.
@simonbs could you consider closing or deconsolidating this thread? It’s getting hard to follow and track down posts.
I’m trying to find the example you mentioned elsewhere re: stacks with no success.
Hello, i made here a widget that displays the currently playing song on spotify using the Last.fm API.
Just create a Last.fm account, connect it to Spotify, create a developer account, and get an API key.
You’ll have to paste you user name and api key in the code.
I couldnt make the widget update more frecuently, if someone can just let me know.
//Replace "USER" with you last.fm user and "APIKEY" with the api key for your user.
let url = "http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=USER&api_key=APIKEY&format=json&limit=1"
let req = new Request(url)
async function createWidget(nowPlaying) {
let widget = new ListWidget()
// load image
const coverArt = await loadImage(nowPlaying.recenttracks.track[0].image[3]["#text"])
widget.backgroundImage = coverArt
widget.addSpacer()
// set gradient background
let startColor = new Color("#1c1c1c19")
let endColor = new Color("#1c1c1cb4")
let gradient = new LinearGradient()
gradient.colors = [startColor, endColor]
gradient.locations = [0.0, 1]
widget.backgroundGradient = gradient
widget.backgroundColor = new Color("1c1c1c")
// add title and artist
let title = nowPlaying.recenttracks.track[0].name.toLowerCase()
// capitalize every first character
title = title.replace(/\b\w/g, function(c) {
return c.toUpperCase();
});
let titleTxt = widget.addText(title)
titleTxt.font = Font.boldSystemFont(12)
titleTxt.textColor = Color.white()
titleTxt.leftAlignText()
widget.addSpacer(2)
let artist = nowPlaying.recenttracks.track[0].artist["#text"]
// capitalize every first character
artist = artist.replace(/\b\w/g, function(c) {
return c.toUpperCase();
});
let artistTxt = widget.addText(artist)
artistTxt.font = Font.systemFont(10)
artistTxt.textColor = Color.yellow()
artistTxt.textOpacity = 1
artistTxt.leftAlignText()
widget.setPadding(8, 15, 10, 5)
widget.url = nowPlaying.recenttracks.track[0].url
return widget
}
// helper function to load and parse a restful json api
async function loadNowPlaying(coverArt) {
const req = new Request(url)
const json = await req.loadJSON()
return json
}
// helper function to download an image from a given url
async function loadImage(imgUrl) {
const url = imgUrl !== null ? imgUrl : placeholder;
const req = new Request(url)
const image = await req.loadImage()
return image
}
const nowPlaying = await loadNowPlaying()
const widget = await createWidget(nowPlaying)
Script.setWidget(widget)
Script.complete()
widget.presentSmall()
Can share your code?
To test the stacks feature I built a Spotify Now Playing widget. It uses the more complex Spotify Authorization Code Flow which is needed to access a user‘s playback information. The widget automatically refreshes expired access tokens. I also built a Siri Shortcut to simplify the initial setup.
Gist and setup instructions:
Caveat:
Even though it’s been a lot of work this widget is more a proof of concept. It will lag behind since it‘s not possible to force an update of widgets and only iOS alone will decide when to refresh.
The Unsplash calendar widget don’t work any more with the pictures.
I think something is wrong with the Unsplash API. If I change the query from “nature” to “nature,water”, it works now.
I think there was probably some problems hours ago with the API. It’s normal now for me.
If you use the script I posted earlier, there’s a spot where you can add your own code. I used @riverwolf’s greeting code for this:
// Get the current date
const date = new Date()
// Format the greeting (thank you riverwolf)
let greeting = "Good "
if (date.getHours() < 6) {
greeting = greeting + "night."
} else if (date.getHours() < 12) {
greeting = greeting + "morning."
} else if (date.getHours() < 17) {
greeting = greeting + "afternoon."
} else if (date.getHours() < 21) {
greeting = greeting + "evening."
} else {
greeting = greeting + "night."
}
// Format the date
let df = new DateFormatter()
df.dateFormat = "EEEE, MMMM d"
// Format the widget
widget.addSpacer(40)
let greetingText = widget.addText(greeting)
greetingText.font = Font.boldSystemFont(36)
greetingText.textColor = Color.white()
let dateText = widget.addText(df.string(date))
dateText.font = Font.regularSystemFont(18)
dateText.textColor = Color.white()
widget.addSpacer()
I think it looks pretty similar:
Hi, I realise I have been a little quiet on here and missed some replies / questions.
I am working on a couple of ideas, in the meantime I have updated my reddit widget. The standard uses the old method, the new uses stacks and has links to open the post too.
Here is a screenshot of the two side by side:
Code can be found here:
RedditViewer:
RedditViewerStacks:
The parameter is as follows:
apollo@scriptable@1@pic@4
The client to open @ the subreddit @ placeholder - this was for debugging @ whether to show pics (this is to be coded) @ How many entries - this will depend on font sizes ‘ verbosity of posts.