project-standalo-sonic-cloud/.workflow/versions/v004/requirements/expanded.yml

754 lines
24 KiB
YAML

feature: "add share music system"
expanded_at: "2025-12-18T18:07:00Z"
mode: full_auto
# ═══════════════════════════════════════════════════════════════
# REQUIREMENTS ANALYSIS
# ═══════════════════════════════════════════════════════════════
analysis:
problem_statement: |
Users and artists currently have no way to share music content from Sonic Cloud
with others outside the platform or to promote their work on social media.
Artists cannot easily grow their audience, and users cannot recommend music to friends.
target_users:
primary:
- Musicians/Artists wanting to promote their work
- Users wanting to share favorite songs with friends
- Playlist curators sharing collections
secondary:
- Social media users discovering shared music
- External website visitors accessing shared links
- Music bloggers embedding content
core_value: |
Enable viral music discovery by making it effortless to share songs, albums, and
playlists through unique shareable links and social media integration. This creates
organic growth loops where shared content brings new users to the platform.
user_stories:
- as: Artist
want: Share my new song on Twitter
so_that: I can promote my music to my followers
- as: User
want: Copy a link to a song
so_that: I can send it to friends via messaging apps
- as: Playlist Curator
want: Share my curated playlist publicly
so_that: Others can discover my music taste
- as: Content Creator
want: Get embed code for a song
so_that: I can include it in my blog post
- as: Artist
want: See how many times my song was shared
so_that: I can measure promotional effectiveness
# ═══════════════════════════════════════════════════════════════
# SCOPE DEFINITION
# ═══════════════════════════════════════════════════════════════
scope:
mvp_features:
# Core Sharing
- feature: Generate shareable links for songs
priority: CRITICAL
complexity: LOW
- feature: Generate shareable links for playlists
priority: CRITICAL
complexity: LOW
- feature: Generate shareable links for albums
priority: CRITICAL
complexity: LOW
- feature: Copy link to clipboard functionality
priority: HIGH
complexity: LOW
# Social Integration
- feature: Share to Twitter with pre-filled text
priority: HIGH
complexity: MEDIUM
- feature: Share to Facebook
priority: HIGH
complexity: MEDIUM
# UI Components
- feature: ShareButton component
priority: CRITICAL
complexity: LOW
description: Clickable button that opens share modal
- feature: ShareModal component
priority: CRITICAL
complexity: MEDIUM
description: Modal displaying share options and generated link
# Landing Page
- feature: Shared content landing page
priority: CRITICAL
complexity: MEDIUM
description: Public page displaying shared song/playlist/album
# Analytics
- feature: Track share creation events
priority: MEDIUM
complexity: LOW
- feature: Track share link clicks
priority: MEDIUM
complexity: MEDIUM
future_features:
# Advanced Sharing
- feature: Embed player code generation
priority: LOW
complexity: HIGH
rationale: "Nice to have but requires iframe security work"
- feature: Share via WhatsApp
priority: MEDIUM
complexity: LOW
rationale: "Popular messaging app, simple URL scheme"
# Advanced Analytics
- feature: Share analytics dashboard
priority: LOW
complexity: HIGH
rationale: "Artists want metrics but complex to build well"
- feature: Geographic share tracking
priority: LOW
complexity: MEDIUM
rationale: "Interesting data but privacy concerns"
# Social Features
- feature: Direct message sharing within platform
priority: MEDIUM
complexity: HIGH
rationale: "Requires building DM system first"
- feature: Share to Instagram Stories
priority: MEDIUM
complexity: HIGH
rationale: "Requires Instagram API approval"
explicitly_excluded:
- NFT/blockchain sharing
- Paid promotion features
- Affiliate link generation
- Cross-platform playlist sync
# ═══════════════════════════════════════════════════════════════
# DATA MODEL
# ═══════════════════════════════════════════════════════════════
data_model:
entities:
- name: Share
table_name: shares
description: Represents a share action creating a unique shareable link
fields:
- name: id
type: String
constraints: "@id @default(cuid())"
- name: type
type: Enum
values: [SONG, PLAYLIST, ALBUM]
description: What type of content is being shared
- name: target_id
type: String
description: ID of the song/playlist/album
- name: token
type: String
constraints: "@unique"
description: Unique URL-safe token for share link
- name: user_id
type: String
constraints: "nullable"
description: User who created the share (null for anonymous)
- name: platform
type: String
constraints: "nullable"
values: [twitter, facebook, clipboard, direct]
description: Platform where share was initiated
- name: created_at
type: DateTime
constraints: "@default(now())"
- name: expires_at
type: DateTime
constraints: "nullable"
description: Optional expiration for temporary shares
relationships:
- field: user
type: User
cardinality: many-to-one
optional: true
- field: clicks
type: ShareClick[]
cardinality: one-to-many
- name: ShareClick
table_name: share_clicks
description: Tracks when someone clicks a share link
fields:
- name: id
type: String
constraints: "@id @default(cuid())"
- name: share_id
type: String
description: Reference to the share
- name: clicked_at
type: DateTime
constraints: "@default(now())"
- name: referrer
type: String
constraints: "nullable"
description: HTTP referrer if available
- name: user_agent
type: String
constraints: "nullable"
description: Browser user agent string
- name: ip_hash
type: String
constraints: "nullable"
description: Hashed IP for privacy-preserving analytics
relationships:
- field: share
type: Share
cardinality: many-to-one
indexes:
- table: shares
fields: [token]
type: unique
- table: shares
fields: [type, target_id]
- table: share_clicks
fields: [share_id, clicked_at]
# ═══════════════════════════════════════════════════════════════
# API DESIGN
# ═══════════════════════════════════════════════════════════════
api_endpoints:
# Share Creation
- id: api_create_song_share
name: Create Song Share
method: POST
path: /api/share/song/:id
purpose: Generate shareable link for a song
authentication: optional
request:
path_params:
- name: id
type: string
description: Song ID
body:
- name: platform
type: string
optional: true
values: [twitter, facebook, clipboard, direct]
response:
success_code: 201
body:
- name: share_token
type: string
description: Unique token for share URL
- name: share_url
type: string
description: Full shareable URL
- name: social_text
type: string
description: Pre-formatted text for social media
errors:
- code: 404
description: Song not found
- code: 403
description: Song is private and user lacks permission
- id: api_create_playlist_share
name: Create Playlist Share
method: POST
path: /api/share/playlist/:id
purpose: Generate shareable link for a playlist
authentication: optional
request:
path_params:
- name: id
type: string
description: Playlist ID
body:
- name: platform
type: string
optional: true
response:
success_code: 201
body:
- name: share_token
type: string
- name: share_url
type: string
- name: social_text
type: string
errors:
- code: 404
description: Playlist not found
- code: 403
description: Playlist is private
- id: api_create_album_share
name: Create Album Share
method: POST
path: /api/share/album/:id
purpose: Generate shareable link for an album
authentication: optional
request:
path_params:
- name: id
type: string
description: Album ID
body:
- name: platform
type: string
optional: true
response:
success_code: 201
body:
- name: share_token
type: string
- name: share_url
type: string
- name: social_text
type: string
errors:
- code: 404
description: Album not found
# Share Resolution
- id: api_resolve_share
name: Resolve Share Token
method: GET
path: /api/share/:token
purpose: Get shared content details and track click
authentication: none
request:
path_params:
- name: token
type: string
description: Share token from URL
response:
success_code: 200
body:
- name: type
type: string
values: [SONG, PLAYLIST, ALBUM]
- name: content
type: object
description: Full content object (Song/Playlist/Album)
- name: share_created_at
type: string
format: iso8601
errors:
- code: 404
description: Share token not found or expired
- code: 410
description: Shared content has been deleted
# Share Analytics
- id: api_get_share_stats
name: Get Share Statistics
method: GET
path: /api/share/:token/stats
purpose: Get analytics for a specific share
authentication: optional
request:
path_params:
- name: token
type: string
response:
success_code: 200
body:
- name: total_clicks
type: integer
- name: unique_clicks
type: integer
description: Estimated unique visitors by IP hash
- name: clicks_by_date
type: array
items:
date: string
count: integer
- name: referrers
type: array
items:
source: string
count: integer
errors:
- code: 404
description: Share not found
- id: api_get_content_shares
name: Get Content Share Summary
method: GET
path: /api/share/summary/:type/:id
purpose: Get share statistics for a song/playlist/album
authentication: required
authorization: Must be content owner or admin
request:
path_params:
- name: type
type: string
values: [song, playlist, album]
- name: id
type: string
description: Content ID
response:
success_code: 200
body:
- name: total_shares
type: integer
- name: total_clicks
type: integer
- name: platforms
type: object
description: Breakdown by platform
- name: recent_shares
type: array
description: Last 10 shares with timestamps
# ═══════════════════════════════════════════════════════════════
# UI STRUCTURE
# ═══════════════════════════════════════════════════════════════
ui_structure:
pages:
- id: page_share_landing
name: Share Landing Page
route: /s/:token
purpose: Public landing page for shared content
components_needed:
- SharedContentHeader
- AudioPlayer (for songs)
- TrackList (for playlists/albums)
- CallToAction (sign up prompt)
features:
- Display shared content with rich metadata
- Play preview if allowed
- Show artist/creator information
- Call to action to join platform
- Open Graph tags for social media previews
accessibility:
- Works without authentication
- Mobile responsive
- Fast loading with SSR
- Proper meta tags for crawlers
components:
- id: component_share_button
name: ShareButton
file_path: components/ShareButton.tsx
purpose: Trigger share modal from any content
props:
- name: type
type: enum
values: [song, playlist, album]
required: true
- name: contentId
type: string
required: true
- name: contentTitle
type: string
required: true
description: Used in share text
- name: artistName
type: string
required: false
description: Used in share text for songs/albums
- name: variant
type: enum
values: [icon, text, full]
default: icon
description: Visual style
behavior:
- onClick opens ShareModal
- Shows loading state during share creation
- Can be used in SongCard, AlbumCard, etc.
- id: component_share_modal
name: ShareModal
file_path: components/ShareModal.tsx
purpose: Display share options and generated link
props:
- name: isOpen
type: boolean
required: true
- name: onClose
type: function
required: true
- name: shareUrl
type: string
required: true
- name: shareText
type: string
required: true
features:
- Copy to clipboard button with feedback
- Social media share buttons
- Generated link display
- QR code (future)
- id: component_social_share_buttons
name: SocialShareButtons
file_path: components/SocialShareButtons.tsx
purpose: Platform-specific share buttons
props:
- name: url
type: string
required: true
- name: text
type: string
required: true
- name: platforms
type: array
default: [twitter, facebook]
behavior:
- Opens platform share dialog in popup
- Tracks platform in analytics
- Falls back to clipboard if platform unavailable
- id: component_shared_content_header
name: SharedContentHeader
file_path: components/SharedContentHeader.tsx
purpose: Header for share landing page
props:
- name: type
type: enum
values: [song, playlist, album]
- name: title
type: string
- name: artist
type: string
optional: true
- name: coverUrl
type: string
optional: true
- name: metadata
type: object
description: Duration, track count, etc.
features:
- Large cover art
- Title and artist
- Metadata display
- Share count badge
# ═══════════════════════════════════════════════════════════════
# SECURITY & AUTHORIZATION
# ═══════════════════════════════════════════════════════════════
security:
authentication:
share_creation: optional
share_access: none
analytics_access: required (owner only)
authorization:
rules:
- resource: Song
action: share
condition: Song is public OR user is owner OR user has access
- resource: Playlist
action: share
condition: Playlist is public OR user is owner
- resource: Album
action: share
condition: Album is public (albums are always public)
- resource: Share Analytics
action: view
condition: User is content owner OR user is admin
token_security:
- Tokens are cryptographically random (cuid)
- Tokens are URL-safe
- No sequential IDs exposed
- Optional expiration dates
- Rate limiting on creation (10/minute per IP)
privacy:
- IP addresses are hashed, not stored
- User agents stored for analytics only
- No cross-site tracking
- Respects Do Not Track header
- GDPR compliant analytics
# ═══════════════════════════════════════════════════════════════
# EDGE CASES & ERROR HANDLING
# ═══════════════════════════════════════════════════════════════
edge_cases:
- scenario: User shares private playlist
detection: Check playlist.is_public flag
handling: Return 403 error with message "Cannot share private playlists"
- scenario: Shared content is deleted
detection: Share exists but target_id has no matching record
handling: Show friendly "Content no longer available" page
- scenario: Share token collision (extremely rare)
detection: Unique constraint violation on token
handling: Retry with new token automatically
- scenario: High share volume (viral content)
detection: Rate limiting triggers
handling: Return 429 with retry-after header
- scenario: Bot traffic clicking shares
detection: Suspicious user agents, high frequency
handling: Track but flag as bot, don't count in "unique clicks"
- scenario: Share link in iframe
detection: X-Frame-Options header
handling: Allow embedding from trusted domains only
- scenario: Expired share token
detection: expires_at < now()
handling: Return 410 Gone status
- scenario: User shares same content multiple times
detection: Check for existing share by user_id + target
handling: Reuse existing share token, update platform
- scenario: Anonymous user shares content
detection: No auth token in request
handling: Allow, set user_id to null
- scenario: Shared song is in private album
detection: Song.album.is_public = false
handling: Still allow sharing individual song if song.is_public = true
# ═══════════════════════════════════════════════════════════════
# ACCEPTANCE CRITERIA
# ═══════════════════════════════════════════════════════════════
acceptance_criteria:
functional:
- Given I am viewing a song, When I click the share button, Then a modal opens with share options
- Given a share modal is open, When I click "Copy Link", Then the link is copied and I see confirmation
- Given a share modal is open, When I click "Twitter", Then Twitter share dialog opens with pre-filled text
- Given I have a share link, When I visit /s/:token, Then I see the shared content
- Given I visit an expired share, When the page loads, Then I see "Content no longer available"
- Given I am the song owner, When I view share stats, Then I see click counts and platforms
- Given I share the same song twice, When I generate the second share, Then I get the same token
non_functional:
- Share link generation completes in < 500ms
- Share landing page loads in < 2s on 3G
- Share tokens are at least 20 characters
- Share modal is keyboard accessible
- Open Graph tags are present for all share pages
- Analytics queries complete in < 1s
security:
- Cannot share private content without permission
- Share tokens are unguessable
- Rate limiting prevents abuse
- IP addresses are hashed, not stored
- Analytics are only visible to content owner
# ═══════════════════════════════════════════════════════════════
# TECHNICAL CONSIDERATIONS
# ═══════════════════════════════════════════════════════════════
technical_notes:
performance:
- Cache share tokens in Redis for fast lookup
- Use database index on shares.token
- Batch insert share clicks for high traffic
- CDN for share landing pages
monitoring:
- Track share creation rate
- Monitor share click patterns
- Alert on unusual traffic spikes
- Dashboard for viral content
testing:
- Unit tests for share token generation
- Integration tests for share flow
- E2E tests for social sharing
- Load testing for viral scenarios
deployment:
- Feature flag for gradual rollout
- A/B test share button placement
- Monitor analytics database load
- Plan for scale to 10k shares/day
# ═══════════════════════════════════════════════════════════════
# SUCCESS METRICS
# ═══════════════════════════════════════════════════════════════
success_metrics:
launch_goals:
- 100 shares created in first week
- 500 share link clicks in first week
- 10% of shared content viewers sign up
- <1% error rate on share creation
ongoing_kpis:
- Daily active shares
- Share-to-click conversion rate
- Click-to-signup conversion rate
- Top shared content
- Platform distribution (Twitter vs Facebook vs direct)
business_impact:
- Increase organic user acquisition by 20%
- Reduce paid marketing cost per acquisition
- Increase artist retention (sharing = promotion)
- Increase content discovery beyond search