754 lines
24 KiB
YAML
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
|