[WIP] music platform user data scraper
teal-fm atproto

wip

Changed files
+439 -5
pages
static
templates
+3
Dockerfile
··· 1 + //TODO will need a node buiilder here for tailwindcss 2 + 3 + 1 4 FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.24.3-alpine3.21 as builder 2 5 3 6 ARG TARGETPLATFORM
+14 -3
README.md
··· 9 9 10 10 well its just a work in progress... we build in the open! 11 11 12 - ## Setup 12 + ## setup 13 13 It is recommend to have port forward url while working with piper. Development or running from docker because of external callbacks. 14 14 15 15 You have a couple of options ··· 44 44 45 45 46 46 47 - #### development 47 + ## development 48 48 49 49 make sure you have your env setup following [the env var setup](#env-variables) 50 50 ··· 69 69 ``` 70 70 air 71 71 ``` 72 + air should automatically build and run piper, and watch for changes on relevant files. 72 73 73 - air should automatically build and run piper, and watch for changes on relevant files. 74 + 75 + ## tailwindcss 76 + 77 + To use tailwindcss you will have to install the tailwindcss cli. This will take the [./pages/static/base.css](./pages/static/base.css) and transform it into a [./pages/static/main.css](./pages/static/main.css) 78 + which is imported on the [./pages/templates/layouts/base.gohtml](./pages/templates/layouts/base.gohtml). When running the dev server tailwindcss will watch for changes and recompile the main.css file. 79 + 80 + 1. Install tailwindcss cli `npm install tailwindcss @tailwindcss/cli` 81 + 2. run `npx @tailwindcss/cli -i ./pages/static/base.css -o ./pages/static/main.css --watch` 82 + 83 + 84 + 74 85 75 86 #### Lexicon changes 76 87 1. Copy the new or changed json schema files to the [lexicon folders](./lexicons)
+418
pages/static/main.css
··· 1 + /*! tailwindcss v4.1.13 | MIT License | https://tailwindcss.com */ 2 + @layer properties; 3 + @layer theme, base, components, utilities; 4 + @layer theme { 5 + :root, :host { 6 + --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", 7 + "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 8 + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", 9 + "Courier New", monospace; 10 + --default-font-family: var(--font-sans); 11 + --default-mono-font-family: var(--font-mono); 12 + } 13 + } 14 + @layer base { 15 + *, ::after, ::before, ::backdrop, ::file-selector-button { 16 + box-sizing: border-box; 17 + margin: 0; 18 + padding: 0; 19 + border: 0 solid; 20 + } 21 + html, :host { 22 + line-height: 1.5; 23 + -webkit-text-size-adjust: 100%; 24 + tab-size: 4; 25 + font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"); 26 + font-feature-settings: var(--default-font-feature-settings, normal); 27 + font-variation-settings: var(--default-font-variation-settings, normal); 28 + -webkit-tap-highlight-color: transparent; 29 + } 30 + hr { 31 + height: 0; 32 + color: inherit; 33 + border-top-width: 1px; 34 + } 35 + abbr:where([title]) { 36 + -webkit-text-decoration: underline dotted; 37 + text-decoration: underline dotted; 38 + } 39 + h1, h2, h3, h4, h5, h6 { 40 + font-size: inherit; 41 + font-weight: inherit; 42 + } 43 + a { 44 + color: inherit; 45 + -webkit-text-decoration: inherit; 46 + text-decoration: inherit; 47 + } 48 + b, strong { 49 + font-weight: bolder; 50 + } 51 + code, kbd, samp, pre { 52 + font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace); 53 + font-feature-settings: var(--default-mono-font-feature-settings, normal); 54 + font-variation-settings: var(--default-mono-font-variation-settings, normal); 55 + font-size: 1em; 56 + } 57 + small { 58 + font-size: 80%; 59 + } 60 + sub, sup { 61 + font-size: 75%; 62 + line-height: 0; 63 + position: relative; 64 + vertical-align: baseline; 65 + } 66 + sub { 67 + bottom: -0.25em; 68 + } 69 + sup { 70 + top: -0.5em; 71 + } 72 + table { 73 + text-indent: 0; 74 + border-color: inherit; 75 + border-collapse: collapse; 76 + } 77 + :-moz-focusring { 78 + outline: auto; 79 + } 80 + progress { 81 + vertical-align: baseline; 82 + } 83 + summary { 84 + display: list-item; 85 + } 86 + ol, ul, menu { 87 + list-style: none; 88 + } 89 + img, svg, video, canvas, audio, iframe, embed, object { 90 + display: block; 91 + vertical-align: middle; 92 + } 93 + img, video { 94 + max-width: 100%; 95 + height: auto; 96 + } 97 + button, input, select, optgroup, textarea, ::file-selector-button { 98 + font: inherit; 99 + font-feature-settings: inherit; 100 + font-variation-settings: inherit; 101 + letter-spacing: inherit; 102 + color: inherit; 103 + border-radius: 0; 104 + background-color: transparent; 105 + opacity: 1; 106 + } 107 + :where(select:is([multiple], [size])) optgroup { 108 + font-weight: bolder; 109 + } 110 + :where(select:is([multiple], [size])) optgroup option { 111 + padding-inline-start: 20px; 112 + } 113 + ::file-selector-button { 114 + margin-inline-end: 4px; 115 + } 116 + ::placeholder { 117 + opacity: 1; 118 + } 119 + @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) { 120 + ::placeholder { 121 + color: currentcolor; 122 + @supports (color: color-mix(in lab, red, red)) { 123 + color: color-mix(in oklab, currentcolor 50%, transparent); 124 + } 125 + } 126 + } 127 + textarea { 128 + resize: vertical; 129 + } 130 + ::-webkit-search-decoration { 131 + -webkit-appearance: none; 132 + } 133 + ::-webkit-date-and-time-value { 134 + min-height: 1lh; 135 + text-align: inherit; 136 + } 137 + ::-webkit-datetime-edit { 138 + display: inline-flex; 139 + } 140 + ::-webkit-datetime-edit-fields-wrapper { 141 + padding: 0; 142 + } 143 + ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field { 144 + padding-block: 0; 145 + } 146 + ::-webkit-calendar-picker-indicator { 147 + line-height: 1; 148 + } 149 + :-moz-ui-invalid { 150 + box-shadow: none; 151 + } 152 + button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button { 153 + appearance: button; 154 + } 155 + ::-webkit-inner-spin-button, ::-webkit-outer-spin-button { 156 + height: auto; 157 + } 158 + [hidden]:where(:not([hidden="until-found"])) { 159 + display: none !important; 160 + } 161 + } 162 + @layer utilities { 163 + .absolute { 164 + position: absolute; 165 + } 166 + .relative { 167 + position: relative; 168 + } 169 + .static { 170 + position: static; 171 + } 172 + .sticky { 173 + position: sticky; 174 + } 175 + .container { 176 + width: 100%; 177 + @media (width >= 40rem) { 178 + max-width: 40rem; 179 + } 180 + @media (width >= 48rem) { 181 + max-width: 48rem; 182 + } 183 + @media (width >= 64rem) { 184 + max-width: 64rem; 185 + } 186 + @media (width >= 80rem) { 187 + max-width: 80rem; 188 + } 189 + @media (width >= 96rem) { 190 + max-width: 96rem; 191 + } 192 + } 193 + .block { 194 + display: block; 195 + } 196 + .contents { 197 + display: contents; 198 + } 199 + .hidden { 200 + display: none; 201 + } 202 + .table { 203 + display: table; 204 + } 205 + .transform { 206 + transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,); 207 + } 208 + .lowercase { 209 + text-transform: lowercase; 210 + } 211 + .filter { 212 + filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); 213 + } 214 + } 215 + body { 216 + font-family: Arial, sans-serif; 217 + max-width: 800px; 218 + margin: 0 auto; 219 + padding: 20px; 220 + line-height: 1.6; 221 + } 222 + h1 { 223 + color: #1DB954; 224 + } 225 + .nav { 226 + display: flex; 227 + flex-wrap: wrap; 228 + margin-bottom: 20px; 229 + } 230 + .nav a { 231 + margin-right: 15px; 232 + margin-bottom: 5px; 233 + text-decoration: none; 234 + color: #1DB954; 235 + font-weight: bold; 236 + } 237 + .card { 238 + border: 1px solid #ddd; 239 + border-radius: 8px; 240 + padding: 20px; 241 + margin-bottom: 20px; 242 + } 243 + .service-status { 244 + font-style: italic; 245 + color: #555; 246 + } 247 + label, input { 248 + display: block; 249 + margin-bottom: 10px; 250 + } 251 + input[type='text'] { 252 + width: 95%; 253 + padding: 8px; 254 + } 255 + input[type='submit'] { 256 + padding: 10px 15px; 257 + color: white; 258 + border: none; 259 + border-radius: 4px; 260 + cursor: pointer; 261 + } 262 + .last-fm-input { 263 + background-color: #d51007; 264 + } 265 + .teal-input { 266 + background-color: #1DB954; 267 + } 268 + .error { 269 + color: red; 270 + margin-bottom: 10px; 271 + } 272 + .lastfm-form { 273 + max-width: 600px; 274 + margin: 20px auto; 275 + padding: 20px; 276 + border: 1px solid #ddd; 277 + border-radius: 8px; 278 + } 279 + .card { 280 + border: 1px solid #ddd; 281 + border-radius: 8px; 282 + padding: 20px; 283 + margin-bottom: 20px; 284 + } 285 + table { 286 + width: 100%; 287 + border-collapse: collapse; 288 + } 289 + table th, table td { 290 + padding: 8px; 291 + text-align: left; 292 + border-bottom: 1px solid #ddd; 293 + } 294 + .key-value { 295 + font-family: monospace; 296 + padding: 10px; 297 + background-color: #f5f5f5; 298 + border: 1px solid #ddd; 299 + border-radius: 4px; 300 + word-break: break-all; 301 + } 302 + .new-key-alert { 303 + background-color: #f8f9fa; 304 + border-left: 4px solid #1DB954; 305 + padding: 15px; 306 + margin-bottom: 20px; 307 + } 308 + .btn { 309 + padding: 8px 16px; 310 + background-color: #1DB954; 311 + color: white; 312 + border: none; 313 + border-radius: 4px; 314 + cursor: pointer; 315 + } 316 + .btn-danger { 317 + background-color: #dc3545; 318 + } 319 + .teal-header { 320 + color: #1DB954; 321 + } 322 + @property --tw-rotate-x { 323 + syntax: "*"; 324 + inherits: false; 325 + } 326 + @property --tw-rotate-y { 327 + syntax: "*"; 328 + inherits: false; 329 + } 330 + @property --tw-rotate-z { 331 + syntax: "*"; 332 + inherits: false; 333 + } 334 + @property --tw-skew-x { 335 + syntax: "*"; 336 + inherits: false; 337 + } 338 + @property --tw-skew-y { 339 + syntax: "*"; 340 + inherits: false; 341 + } 342 + @property --tw-blur { 343 + syntax: "*"; 344 + inherits: false; 345 + } 346 + @property --tw-brightness { 347 + syntax: "*"; 348 + inherits: false; 349 + } 350 + @property --tw-contrast { 351 + syntax: "*"; 352 + inherits: false; 353 + } 354 + @property --tw-grayscale { 355 + syntax: "*"; 356 + inherits: false; 357 + } 358 + @property --tw-hue-rotate { 359 + syntax: "*"; 360 + inherits: false; 361 + } 362 + @property --tw-invert { 363 + syntax: "*"; 364 + inherits: false; 365 + } 366 + @property --tw-opacity { 367 + syntax: "*"; 368 + inherits: false; 369 + } 370 + @property --tw-saturate { 371 + syntax: "*"; 372 + inherits: false; 373 + } 374 + @property --tw-sepia { 375 + syntax: "*"; 376 + inherits: false; 377 + } 378 + @property --tw-drop-shadow { 379 + syntax: "*"; 380 + inherits: false; 381 + } 382 + @property --tw-drop-shadow-color { 383 + syntax: "*"; 384 + inherits: false; 385 + } 386 + @property --tw-drop-shadow-alpha { 387 + syntax: "<percentage>"; 388 + inherits: false; 389 + initial-value: 100%; 390 + } 391 + @property --tw-drop-shadow-size { 392 + syntax: "*"; 393 + inherits: false; 394 + } 395 + @layer properties { 396 + @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) { 397 + *, ::before, ::after, ::backdrop { 398 + --tw-rotate-x: initial; 399 + --tw-rotate-y: initial; 400 + --tw-rotate-z: initial; 401 + --tw-skew-x: initial; 402 + --tw-skew-y: initial; 403 + --tw-blur: initial; 404 + --tw-brightness: initial; 405 + --tw-contrast: initial; 406 + --tw-grayscale: initial; 407 + --tw-hue-rotate: initial; 408 + --tw-invert: initial; 409 + --tw-opacity: initial; 410 + --tw-saturate: initial; 411 + --tw-sepia: initial; 412 + --tw-drop-shadow: initial; 413 + --tw-drop-shadow-color: initial; 414 + --tw-drop-shadow-alpha: 100%; 415 + --tw-drop-shadow-size: initial; 416 + } 417 + } 418 + }
+2
pages/static/styles.css pages/static/base.css
··· 1 + @import "tailwindcss"; 2 + 1 3 body { 2 4 font-family: Arial, sans-serif; 3 5 max-width: 800px;
+1 -1
pages/templates/home.gohtml
··· 6 6 7 7 8 8 <div class="card"> 9 - <h2>Welcome to Piper</h2> 9 + <h2 class="">Welcome to Piper</h2> 10 10 <p>Piper is a multi-user application that records what you're listening to on Spotify and Last.fm, saving your listening history.</p> 11 11 12 12 {{if .NavBar.IsLoggedIn}}
+1 -1
pages/templates/layouts/base.gohtml
··· 3 3 <html lang="en"> 4 4 <head> 5 5 <title>Piper - Spotify & Last.fm Tracker</title> 6 - <link rel="stylesheet" href="/static/styles.css"> 6 + <link rel="stylesheet" href="/static/main.css"> 7 7 </head> 8 8 <body> 9 9 {{ block "content" . }}{{ end }}