Add basic spotify page

It currently doesn't handle pagination, or caching
This commit is contained in:
Jake Howard 2022-08-17 22:17:32 +01:00
parent 9f2b2a81c9
commit ace30f4dbd
Signed by: jake
GPG Key ID: 57AFB45680EDD477
11 changed files with 1100 additions and 0 deletions

View File

@ -0,0 +1,9 @@
body.page-spotifyplaylistpage {
table {
img {
min-width: 55px;
width: 55px;
height: 55px;
}
}
}

View File

@ -15,6 +15,7 @@
@import "blocks";
@import "shareon";
@import "search";
@import "spotify";
html,
body {

View File

@ -9,6 +9,7 @@ env = environ.Env(
DEBUG=(bool, False),
BASE_HOSTNAME=(str, "example.com"),
UNSPLASH_CLIENT_ID=(str, ""),
SPOTIFY_PROXY_HOST=(str, ""),
)
# Read local secrets
@ -30,6 +31,7 @@ INSTALLED_APPS = [
"website.blog",
"website.images",
"website.contact",
"website.spotify",
"website.contrib.code_block",
"website.contrib.mermaid_block",
"website.contrib.unsplash",
@ -178,6 +180,7 @@ WAGTAILEMBEDS_FINDERS = [
]
UNSPLASH_CLIENT_ID = env("UNSPLASH_CLIENT_ID")
SPOTIFY_PROXY_HOST = env("SPOTIFY_PROXY_HOST")
if DEBUG:

View File

16
website/spotify/client.py Normal file
View File

@ -0,0 +1,16 @@
import requests
from django.conf import settings
def is_valid_playlist(playlist_id: str) -> bool:
return requests.get(
f"https://{settings.SPOTIFY_PROXY_HOST}/v1/playlists/{playlist_id}"
).ok
def get_playlist(playlist_id: str) -> dict:
response = requests.get(
f"https://{settings.SPOTIFY_PROXY_HOST}/v1/playlists/{playlist_id}"
)
response.raise_for_status()
return response.json()

View File

@ -0,0 +1,989 @@
# Generated by Django 4.0.6 on 2022-08-17 20:20
import django.db.models.deletion
import wagtail.blocks
import wagtail.embeds.blocks
import wagtail.fields
import wagtail.images.blocks
from django.db import migrations, models
import website.spotify.models
class Migration(migrations.Migration):
initial = True
dependencies = [
("unsplash", "0002_unsplashphoto_created_and_more"),
("wagtailcore", "0069_log_entry_jsonfield"),
("images", "0001_initial"),
]
operations = [
migrations.CreateModel(
name="SpotifyPlaylistPage",
fields=[
(
"page_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="wagtailcore.page",
),
),
("subtitle", models.CharField(blank=True, max_length=255)),
(
"body",
wagtail.fields.StreamField(
[
("embed", wagtail.embeds.blocks.EmbedBlock()),
(
"rich_text",
wagtail.blocks.RichTextBlock(
features=[
"h2",
"h3",
"h4",
"h5",
"h6",
"bold",
"italic",
"ol",
"ul",
"link",
"document-link",
"code",
"strikethrough",
"snippet-link",
"snippet-embed",
]
),
),
(
"lorem",
wagtail.blocks.StructBlock(
[
(
"paragraphs",
wagtail.blocks.IntegerBlock(min_value=1),
)
]
),
),
("html", wagtail.blocks.RawHTMLBlock()),
(
"image",
wagtail.blocks.StructBlock(
[
(
"image",
wagtail.images.blocks.ImageChooserBlock(),
),
(
"caption",
wagtail.blocks.RichTextBlock(
features=[
"bold",
"italic",
"link",
"document-link",
"code",
"strikethrough",
"snippet-link",
]
),
),
]
),
),
(
"code",
wagtail.blocks.StructBlock(
[
(
"language",
wagtail.blocks.ChoiceBlock(
choices=[
("ABAP", "ABAP"),
("ABNF", "ABNF"),
("ADL", "ADL"),
("AMDGPU", "AMDGPU"),
(
"ANSYS parametric design language",
"ANSYS parametric design language",
),
("ANTLR", "ANTLR"),
(
"ANTLR With ActionScript Target",
"ANTLR With ActionScript Target",
),
(
"ANTLR With C# Target",
"ANTLR With C# Target",
),
(
"ANTLR With CPP Target",
"ANTLR With CPP Target",
),
(
"ANTLR With Java Target",
"ANTLR With Java Target",
),
(
"ANTLR With ObjectiveC Target",
"ANTLR With ObjectiveC Target",
),
(
"ANTLR With Perl Target",
"ANTLR With Perl Target",
),
(
"ANTLR With Python Target",
"ANTLR With Python Target",
),
(
"ANTLR With Ruby Target",
"ANTLR With Ruby Target",
),
("APL", "APL"),
("ASCII armored", "ASCII armored"),
("ActionScript", "ActionScript"),
(
"ActionScript 3",
"ActionScript 3",
),
("Ada", "Ada"),
("Agda", "Agda"),
("Aheui", "Aheui"),
("Alloy", "Alloy"),
("AmbientTalk", "AmbientTalk"),
("Ampl", "Ampl"),
("Angular2", "Angular2"),
("ApacheConf", "ApacheConf"),
("AppleScript", "AppleScript"),
("Arduino", "Arduino"),
("Arrow", "Arrow"),
("AspectJ", "AspectJ"),
("Asymptote", "Asymptote"),
("Augeas", "Augeas"),
("AutoIt", "AutoIt"),
("Awk", "Awk"),
("BARE", "BARE"),
("BBC Basic", "BBC Basic"),
("BBCode", "BBCode"),
("BC", "BC"),
("BNF", "BNF"),
("BST", "BST"),
("BUGS", "BUGS"),
("Base Makefile", "Base Makefile"),
("Bash", "Bash"),
("Bash Session", "Bash Session"),
("Batchfile", "Batchfile"),
("Bdd", "Bdd"),
("Befunge", "Befunge"),
("Berry", "Berry"),
("BibTeX", "BibTeX"),
("BlitzBasic", "BlitzBasic"),
("BlitzMax", "BlitzMax"),
("Boa", "Boa"),
("Boo", "Boo"),
("Boogie", "Boogie"),
("Brainfuck", "Brainfuck"),
("C", "C"),
("C#", "C#"),
("C++", "C++"),
("CAmkES", "CAmkES"),
("CBM BASIC V2", "CBM BASIC V2"),
("CDDL", "CDDL"),
("CFEngine3", "CFEngine3"),
("CMake", "CMake"),
("COBOL", "COBOL"),
("COBOLFree", "COBOLFree"),
("CPSA", "CPSA"),
("CSS", "CSS"),
(
"CSS+Django/Jinja",
"CSS+Django/Jinja",
),
(
"CSS+Genshi Text",
"CSS+Genshi Text",
),
("CSS+Lasso", "CSS+Lasso"),
("CSS+Mako", "CSS+Mako"),
("CSS+Myghty", "CSS+Myghty"),
("CSS+PHP", "CSS+PHP"),
("CSS+Ruby", "CSS+Ruby"),
("CSS+Smarty", "CSS+Smarty"),
("CSS+UL4", "CSS+UL4"),
(
"CSS+mozpreproc",
"CSS+mozpreproc",
),
("CUDA", "CUDA"),
("Cap'n Proto", "Cap'n Proto"),
("CapDL", "CapDL"),
("Ceylon", "Ceylon"),
("ChaiScript", "ChaiScript"),
("Chapel", "Chapel"),
("Charmci", "Charmci"),
("Cheetah", "Cheetah"),
("Cirru", "Cirru"),
("Clay", "Clay"),
("Clean", "Clean"),
("Clojure", "Clojure"),
("ClojureScript", "ClojureScript"),
("CoffeeScript", "CoffeeScript"),
(
"Coldfusion CFC",
"Coldfusion CFC",
),
(
"Coldfusion HTML",
"Coldfusion HTML",
),
("Common Lisp", "Common Lisp"),
(
"Component Pascal",
"Component Pascal",
),
("Coq", "Coq"),
("Crmsh", "Crmsh"),
("Croc", "Croc"),
("Cryptol", "Cryptol"),
("Crystal", "Crystal"),
(
"Csound Document",
"Csound Document",
),
(
"Csound Orchestra",
"Csound Orchestra",
),
("Csound Score", "Csound Score"),
("Cypher", "Cypher"),
("Cython", "Cython"),
("D", "D"),
("DASM16", "DASM16"),
("DTD", "DTD"),
("Darcs Patch", "Darcs Patch"),
("Dart", "Dart"),
(
"Debian Control file",
"Debian Control file",
),
(
"Debian Sourcelist",
"Debian Sourcelist",
),
("Delphi", "Delphi"),
("Devicetree", "Devicetree"),
("Diff", "Diff"),
("Django/Jinja", "Django/Jinja"),
("Docker", "Docker"),
("Duel", "Duel"),
("Dylan", "Dylan"),
("Dylan session", "Dylan session"),
("DylanLID", "DylanLID"),
("E-mail", "E-mail"),
("EBNF", "EBNF"),
("ECL", "ECL"),
("ERB", "ERB"),
("Earl Grey", "Earl Grey"),
("Easytrieve", "Easytrieve"),
("Eiffel", "Eiffel"),
("Elixir", "Elixir"),
(
"Elixir iex session",
"Elixir iex session",
),
("Elm", "Elm"),
("Elpi", "Elpi"),
("EmacsLisp", "EmacsLisp"),
(
"Embedded Ragel",
"Embedded Ragel",
),
("Erlang", "Erlang"),
(
"Erlang erl session",
"Erlang erl session",
),
("Evoque", "Evoque"),
("Ezhil", "Ezhil"),
("F#", "F#"),
("FStar", "FStar"),
("Factor", "Factor"),
("Fancy", "Fancy"),
("Fantom", "Fantom"),
("Felix", "Felix"),
("Fennel", "Fennel"),
("Fish", "Fish"),
("Flatline", "Flatline"),
("FloScript", "FloScript"),
("Forth", "Forth"),
("Fortran", "Fortran"),
("FortranFixed", "FortranFixed"),
("FoxPro", "FoxPro"),
("Freefem", "Freefem"),
("Futhark", "Futhark"),
("GAP", "GAP"),
("GAS", "GAS"),
("GDScript", "GDScript"),
("GLSL", "GLSL"),
("GSQL", "GSQL"),
("Genshi", "Genshi"),
("Genshi Text", "Genshi Text"),
(
"Gettext Catalog",
"Gettext Catalog",
),
("Gherkin", "Gherkin"),
("Gnuplot", "Gnuplot"),
("Go", "Go"),
("Golo", "Golo"),
("GoodData-CL", "GoodData-CL"),
("Gosu", "Gosu"),
("Gosu Template", "Gosu Template"),
("Graphviz", "Graphviz"),
("Groff", "Groff"),
("Groovy", "Groovy"),
("HLSL", "HLSL"),
("HSAIL", "HSAIL"),
("HTML", "HTML"),
(
"HTML + Angular2",
"HTML + Angular2",
),
("HTML+Cheetah", "HTML+Cheetah"),
(
"HTML+Django/Jinja",
"HTML+Django/Jinja",
),
("HTML+Evoque", "HTML+Evoque"),
("HTML+Genshi", "HTML+Genshi"),
(
"HTML+Handlebars",
"HTML+Handlebars",
),
("HTML+Lasso", "HTML+Lasso"),
("HTML+Mako", "HTML+Mako"),
("HTML+Myghty", "HTML+Myghty"),
("HTML+PHP", "HTML+PHP"),
("HTML+Smarty", "HTML+Smarty"),
("HTML+Twig", "HTML+Twig"),
("HTML+UL4", "HTML+UL4"),
("HTML+Velocity", "HTML+Velocity"),
("HTTP", "HTTP"),
("Haml", "Haml"),
("Handlebars", "Handlebars"),
("Haskell", "Haskell"),
("Haxe", "Haxe"),
("Hexdump", "Hexdump"),
("Hspec", "Hspec"),
("Hxml", "Hxml"),
("Hy", "Hy"),
("Hybris", "Hybris"),
("IDL", "IDL"),
("INI", "INI"),
("IRC logs", "IRC logs"),
("Icon", "Icon"),
("Idris", "Idris"),
("Igor", "Igor"),
("Inform 6", "Inform 6"),
(
"Inform 6 template",
"Inform 6 template",
),
("Inform 7", "Inform 7"),
("Io", "Io"),
("Ioke", "Ioke"),
("Isabelle", "Isabelle"),
("J", "J"),
("JAGS", "JAGS"),
("JCL", "JCL"),
("JSGF", "JSGF"),
("JSLT", "JSLT"),
("JSON", "JSON"),
("JSON-LD", "JSON-LD"),
(
"JSONBareObject",
"JSONBareObject",
),
("Jasmin", "Jasmin"),
("Java", "Java"),
(
"Java Server Page",
"Java Server Page",
),
("JavaScript", "JavaScript"),
(
"JavaScript+Cheetah",
"JavaScript+Cheetah",
),
(
"JavaScript+Django/Jinja",
"JavaScript+Django/Jinja",
),
(
"JavaScript+Genshi Text",
"JavaScript+Genshi Text",
),
(
"JavaScript+Lasso",
"JavaScript+Lasso",
),
(
"JavaScript+Mako",
"JavaScript+Mako",
),
(
"JavaScript+Myghty",
"JavaScript+Myghty",
),
(
"JavaScript+PHP",
"JavaScript+PHP",
),
(
"JavaScript+Ruby",
"JavaScript+Ruby",
),
(
"JavaScript+Smarty",
"JavaScript+Smarty",
),
(
"Javascript+UL4",
"Javascript+UL4",
),
(
"Javascript+mozpreproc",
"Javascript+mozpreproc",
),
("Julia", "Julia"),
("Julia console", "Julia console"),
("Juttle", "Juttle"),
("K", "K"),
("Kal", "Kal"),
("Kconfig", "Kconfig"),
("Kernel log", "Kernel log"),
("Koka", "Koka"),
("Kotlin", "Kotlin"),
("Kuin", "Kuin"),
("LLVM", "LLVM"),
("LLVM-MIR", "LLVM-MIR"),
("LLVM-MIR Body", "LLVM-MIR Body"),
("LSL", "LSL"),
("Lasso", "Lasso"),
("Lean", "Lean"),
("LessCss", "LessCss"),
(
"Lighttpd configuration file",
"Lighttpd configuration file",
),
("LilyPond", "LilyPond"),
("Limbo", "Limbo"),
("Literate Agda", "Literate Agda"),
(
"Literate Cryptol",
"Literate Cryptol",
),
(
"Literate Haskell",
"Literate Haskell",
),
(
"Literate Idris",
"Literate Idris",
),
("LiveScript", "LiveScript"),
("Logos", "Logos"),
("Logtalk", "Logtalk"),
("Lua", "Lua"),
("MAQL", "MAQL"),
("MCFunction", "MCFunction"),
("MIME", "MIME"),
("MOOCode", "MOOCode"),
("MQL", "MQL"),
("MSDOS Session", "MSDOS Session"),
("MXML", "MXML"),
("Macaulay2", "Macaulay2"),
("Makefile", "Makefile"),
("Mako", "Mako"),
("Markdown", "Markdown"),
("Mask", "Mask"),
("Mason", "Mason"),
("Mathematica", "Mathematica"),
("Matlab", "Matlab"),
(
"Matlab session",
"Matlab session",
),
("Maxima", "Maxima"),
("Meson", "Meson"),
("MiniD", "MiniD"),
("MiniScript", "MiniScript"),
("Modelica", "Modelica"),
("Modula-2", "Modula-2"),
(
"MoinMoin/Trac Wiki markup",
"MoinMoin/Trac Wiki markup",
),
("Monkey", "Monkey"),
("Monte", "Monte"),
("MoonScript", "MoonScript"),
("Mosel", "Mosel"),
("Mscgen", "Mscgen"),
("MuPAD", "MuPAD"),
("MySQL", "MySQL"),
("Myghty", "Myghty"),
("NASM", "NASM"),
("NCL", "NCL"),
("NSIS", "NSIS"),
("Nemerle", "Nemerle"),
("NestedText", "NestedText"),
("NewLisp", "NewLisp"),
("Newspeak", "Newspeak"),
(
"Nginx configuration file",
"Nginx configuration file",
),
("Nimrod", "Nimrod"),
("Nit", "Nit"),
("Nix", "Nix"),
(
"Node.js REPL console session",
"Node.js REPL console session",
),
("Notmuch", "Notmuch"),
("NuSMV", "NuSMV"),
("NumPy", "NumPy"),
("OCaml", "OCaml"),
("ODIN", "ODIN"),
(
"OMG Interface Definition Language",
"OMG Interface Definition Language",
),
("Objective-C", "Objective-C"),
("Objective-C++", "Objective-C++"),
("Objective-J", "Objective-J"),
("Octave", "Octave"),
("Ooc", "Ooc"),
("Opa", "Opa"),
("OpenEdge ABL", "OpenEdge ABL"),
("PEG", "PEG"),
("PHP", "PHP"),
("PL/pgSQL", "PL/pgSQL"),
("POVRay", "POVRay"),
("PacmanConf", "PacmanConf"),
("Pan", "Pan"),
("ParaSail", "ParaSail"),
("Pawn", "Pawn"),
("Perl", "Perl"),
("Perl6", "Perl6"),
("Pig", "Pig"),
("Pike", "Pike"),
("PkgConfig", "PkgConfig"),
("Pointless", "Pointless"),
("Pony", "Pony"),
("PostScript", "PostScript"),
(
"PostgreSQL SQL dialect",
"PostgreSQL SQL dialect",
),
(
"PostgreSQL console (psql)",
"PostgreSQL console (psql)",
),
("PowerShell", "PowerShell"),
(
"PowerShell Session",
"PowerShell Session",
),
("Praat", "Praat"),
("Procfile", "Procfile"),
("Prolog", "Prolog"),
("PromQL", "PromQL"),
("Properties", "Properties"),
(
"Protocol Buffer",
"Protocol Buffer",
),
(
"PsySH console session for PHP",
"PsySH console session for PHP",
),
("Pug", "Pug"),
("Puppet", "Puppet"),
("PyPy Log", "PyPy Log"),
("Python", "Python"),
("Python 2.x", "Python 2.x"),
(
"Python 2.x Traceback",
"Python 2.x Traceback",
),
(
"Python Traceback",
"Python Traceback",
),
(
"Python console session",
"Python console session",
),
("Python+UL4", "Python+UL4"),
("Q", "Q"),
("QBasic", "QBasic"),
("QML", "QML"),
("QVTO", "QVTO"),
("Qlik", "Qlik"),
("RConsole", "RConsole"),
("REBOL", "REBOL"),
("RHTML", "RHTML"),
("RPMSpec", "RPMSpec"),
("RQL", "RQL"),
("RSL", "RSL"),
("Racket", "Racket"),
("Ragel", "Ragel"),
(
"Ragel in C Host",
"Ragel in C Host",
),
(
"Ragel in CPP Host",
"Ragel in CPP Host",
),
(
"Ragel in D Host",
"Ragel in D Host",
),
(
"Ragel in Java Host",
"Ragel in Java Host",
),
(
"Ragel in Objective C Host",
"Ragel in Objective C Host",
),
(
"Ragel in Ruby Host",
"Ragel in Ruby Host",
),
(
"Raw token data",
"Raw token data",
),
("Rd", "Rd"),
("ReasonML", "ReasonML"),
("Red", "Red"),
("Redcode", "Redcode"),
(
"Relax-NG Compact",
"Relax-NG Compact",
),
(
"ResourceBundle",
"ResourceBundle",
),
("Rexx", "Rexx"),
("Ride", "Ride"),
("Rita", "Rita"),
(
"Roboconf Graph",
"Roboconf Graph",
),
(
"Roboconf Instances",
"Roboconf Instances",
),
(
"RobotFramework",
"RobotFramework",
),
("Ruby", "Ruby"),
(
"Ruby irb session",
"Ruby irb session",
),
("Rust", "Rust"),
("S", "S"),
("SARL", "SARL"),
("SAS", "SAS"),
("SCSS", "SCSS"),
("SNBT", "SNBT"),
("SPARQL", "SPARQL"),
("SQL", "SQL"),
("SWIG", "SWIG"),
("Sass", "Sass"),
("Savi", "Savi"),
("Scala", "Scala"),
(
"Scalate Server Page",
"Scalate Server Page",
),
("Scaml", "Scaml"),
("Scheme", "Scheme"),
("Scilab", "Scilab"),
("Sed", "Sed"),
("ShExC", "ShExC"),
("Shen", "Shen"),
("Sieve", "Sieve"),
("Silver", "Silver"),
("Singularity", "Singularity"),
("Slash", "Slash"),
("Slim", "Slim"),
("Slurm", "Slurm"),
("Smali", "Smali"),
("Smalltalk", "Smalltalk"),
(
"SmartGameFormat",
"SmartGameFormat",
),
("Smarty", "Smarty"),
("Smithy", "Smithy"),
("Snobol", "Snobol"),
("Snowball", "Snowball"),
("Solidity", "Solidity"),
("Sophia", "Sophia"),
("SourcePawn", "SourcePawn"),
("Spice", "Spice"),
("SquidConf", "SquidConf"),
("Srcinfo", "Srcinfo"),
("Stan", "Stan"),
("Standard ML", "Standard ML"),
("Stata", "Stata"),
("SuperCollider", "SuperCollider"),
("Swift", "Swift"),
("TADS 3", "TADS 3"),
("TAP", "TAP"),
("TASM", "TASM"),
("TOML", "TOML"),
("Tal", "Tal"),
("Tcl", "Tcl"),
("Tcsh", "Tcsh"),
("Tcsh Session", "Tcsh Session"),
("TeX", "TeX"),
("Tea", "Tea"),
(
"Tera Term macro",
"Tera Term macro",
),
("Termcap", "Termcap"),
("Terminfo", "Terminfo"),
("Terraform", "Terraform"),
("Text only", "Text only"),
("Text output", "Text output"),
("ThingsDB", "ThingsDB"),
("Thrift", "Thrift"),
("Todotxt", "Todotxt"),
("TrafficScript", "TrafficScript"),
("Transact-SQL", "Transact-SQL"),
("Treetop", "Treetop"),
("Turtle", "Turtle"),
("Twig", "Twig"),
("TypeScript", "TypeScript"),
("TypoScript", "TypoScript"),
(
"TypoScriptCssData",
"TypoScriptCssData",
),
(
"TypoScriptHtmlData",
"TypoScriptHtmlData",
),
(
"Typographic Number Theory",
"Typographic Number Theory",
),
("UL4", "UL4"),
("USD", "USD"),
("Unicon", "Unicon"),
(
"Unix/Linux config files",
"Unix/Linux config files",
),
("UrbiScript", "UrbiScript"),
("VB.net", "VB.net"),
("VBScript", "VBScript"),
("VCL", "VCL"),
("VCLSnippets", "VCLSnippets"),
("VCTreeStatus", "VCTreeStatus"),
("VGL", "VGL"),
("Vala", "Vala"),
("Velocity", "Velocity"),
("VimL", "VimL"),
("WDiff", "WDiff"),
("Web IDL", "Web IDL"),
("WebAssembly", "WebAssembly"),
("Whiley", "Whiley"),
("X10", "X10"),
("XML", "XML"),
("XML+Cheetah", "XML+Cheetah"),
(
"XML+Django/Jinja",
"XML+Django/Jinja",
),
("XML+Evoque", "XML+Evoque"),
("XML+Lasso", "XML+Lasso"),
("XML+Mako", "XML+Mako"),
("XML+Myghty", "XML+Myghty"),
("XML+PHP", "XML+PHP"),
("XML+Ruby", "XML+Ruby"),
("XML+Smarty", "XML+Smarty"),
("XML+UL4", "XML+UL4"),
("XML+Velocity", "XML+Velocity"),
("XQuery", "XQuery"),
("XSLT", "XSLT"),
(
"XUL+mozpreproc",
"XUL+mozpreproc",
),
("Xorg", "Xorg"),
("Xtend", "Xtend"),
("YAML", "YAML"),
("YAML+Jinja", "YAML+Jinja"),
("YANG", "YANG"),
("Zeek", "Zeek"),
("Zephir", "Zephir"),
("Zig", "Zig"),
("aspx-cs", "aspx-cs"),
("aspx-vb", "aspx-vb"),
("autohotkey", "autohotkey"),
("c-objdump", "c-objdump"),
("cADL", "cADL"),
(
"ca65 assembler",
"ca65 assembler",
),
("cfstatement", "cfstatement"),
("cplint", "cplint"),
("cpp-objdump", "cpp-objdump"),
("d-objdump", "d-objdump"),
("dg", "dg"),
("eC", "eC"),
("execline", "execline"),
("g-code", "g-code"),
("liquid", "liquid"),
(
"mozhashpreproc",
"mozhashpreproc",
),
(
"mozpercentpreproc",
"mozpercentpreproc",
),
("nesC", "nesC"),
("objdump", "objdump"),
("objdump-nasm", "objdump-nasm"),
(
"reStructuredText",
"reStructuredText",
),
("reg", "reg"),
("scdoc", "scdoc"),
("sqlite3con", "sqlite3con"),
("systemverilog", "systemverilog"),
("teal", "teal"),
("tiddler", "tiddler"),
("ucode", "ucode"),
("verilog", "verilog"),
("vhdl", "vhdl"),
("xtlang", "xtlang"),
]
),
),
("source", wagtail.blocks.TextBlock()),
]
),
),
(
"tangent",
wagtail.blocks.StructBlock(
[
(
"name",
wagtail.blocks.CharBlock(max_length=64),
),
(
"content",
wagtail.blocks.RichTextBlock(
features=[
"bold",
"italic",
"ol",
"ul",
"link",
"document-link",
"code",
"strikethrough",
"snippet-link",
]
),
),
]
),
),
(
"mermaid",
wagtail.blocks.StructBlock(
[
("source", wagtail.blocks.TextBlock()),
(
"caption",
wagtail.blocks.RichTextBlock(
features=[
"bold",
"italic",
"link",
"document-link",
"code",
"strikethrough",
"snippet-link",
]
),
),
]
),
),
],
blank=True,
use_json_field=True,
),
),
(
"spotify_playlist_id",
models.CharField(
max_length=32,
unique=True,
validators=[website.spotify.models.validate_playlist_id],
),
),
(
"hero_image",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="images.customimage",
),
),
(
"hero_unsplash_photo",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="unsplash.unsplashphoto",
),
),
],
options={
"abstract": False,
},
bases=("wagtailcore.page",),
),
]

View File

38
website/spotify/models.py Normal file
View File

@ -0,0 +1,38 @@
from django.core.exceptions import ValidationError
from django.db import models
from django.http.request import HttpRequest
from wagtail.admin.panels import FieldPanel
from website.common.models import BaseContentPage
from . import client
def validate_playlist_id(playlist_id: str) -> None:
if not client.is_valid_playlist(playlist_id):
raise ValidationError("Unknown playlist id")
class SpotifyPlaylistPage(BaseContentPage):
subpage_types: list = []
spotify_playlist_id = models.CharField(
max_length=32, unique=True, validators=[validate_playlist_id]
)
content_panels = BaseContentPage.content_panels + [
FieldPanel("spotify_playlist_id")
]
@property
def table_of_contents(self) -> list:
return []
@property
def reading_time(self) -> int:
return 0
def get_context(self, request: HttpRequest) -> dict:
context = super().get_context(request)
context["playlist"] = client.get_playlist(self.spotify_playlist_id)
return context

View File

@ -0,0 +1,17 @@
{% with url=track.external_urls.spotify %}
<tr>
<td>
<a href="{{ url }}">
<img src="{{ track.album.images.0.url }}" decoding="async" loading="lazy" referrerpolicy="no-referrer" class="image"/>
</a>
</td>
<td>
{% if track.preview_url %}
<a href="{{ track.preview_url }}"><i class="fas fa-play"></i></a>
{% endif %}
</td>
<td><a href="{{ url }}">{{ track.name }}</a></td>
<td>{% for artist in track.artists %}{% if not forloop.first %}, {% endif %}{{ artist.name }}{% endfor %}</td>
<td>{{ track.album.name }}</td>
</tr>
{% endwith %}

View File

@ -0,0 +1,3 @@
{% for track in tracks %}
{% include "spotify/spotify-item.html" with track=track.track %}
{% endfor %}

View File

@ -0,0 +1,24 @@
{% extends "common/content_page.html" %}
{% block post_content %}
<section class="container content">
<h3>{{ playlist.name }}</h3>
<table class="table">
<thead>
<tr>
<th></th>
<th></th>
<th>Track</th>
<th>Artist</th>
<th>Album</th>
</tr>
</thead>
<tbody>
{% include "spotify/spotify-items.html" with tracks=playlist.tracks.items %}
</tbody>
</table>
</section>
<section class="container">
<a href="{{ playlist.external_urls.spotify }}"><i class="fab fa-spotify"></i> View on Spotify</a>
</section>
{% endblock %}