Sync current dev state
This commit is contained in:
594
docs/QUICK_START_GUIDE.md
Normal file
594
docs/QUICK_START_GUIDE.md
Normal file
@@ -0,0 +1,594 @@
|
||||
# EasyStream API Quick Start Guide
|
||||
|
||||
## Get Started in 5 Minutes
|
||||
|
||||
This guide will get you up and running with the EasyStream API quickly.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- EasyStream installed and running
|
||||
- Modern web browser
|
||||
- Basic JavaScript knowledge
|
||||
|
||||
---
|
||||
|
||||
## 1. Authentication
|
||||
|
||||
### Login and Get Token
|
||||
|
||||
```javascript
|
||||
// The API client is automatically available as 'api'
|
||||
const result = await api.login('myusername', 'mypassword');
|
||||
|
||||
if (result.success) {
|
||||
console.log('Logged in!');
|
||||
console.log('User:', result.user);
|
||||
console.log('Token:', result.token);
|
||||
// Token is automatically stored
|
||||
}
|
||||
```
|
||||
|
||||
### Check if Logged In
|
||||
|
||||
```javascript
|
||||
if (api.isAuthenticated()) {
|
||||
console.log('User is logged in');
|
||||
} else {
|
||||
console.log('Please log in');
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Working with Videos
|
||||
|
||||
### List Videos
|
||||
|
||||
```javascript
|
||||
const videos = await api.getVideos({
|
||||
page: 1,
|
||||
limit: 20,
|
||||
sort: 'popular'
|
||||
});
|
||||
|
||||
console.log('Videos:', videos.data.videos);
|
||||
```
|
||||
|
||||
### Get Single Video
|
||||
|
||||
```javascript
|
||||
const video = await api.getVideo('123456');
|
||||
console.log('Video:', video.data);
|
||||
```
|
||||
|
||||
### Search Videos
|
||||
|
||||
```javascript
|
||||
const results = await api.searchVideos('funny cats');
|
||||
console.log('Found:', results.data.videos);
|
||||
```
|
||||
|
||||
### Like a Video
|
||||
|
||||
```javascript
|
||||
await api.likeVideo('123456', 'like');
|
||||
console.log('Video liked!');
|
||||
```
|
||||
|
||||
### Create a Video
|
||||
|
||||
```javascript
|
||||
const newVideo = await api.createVideo({
|
||||
title: 'My Awesome Video',
|
||||
description: 'This is a great video',
|
||||
privacy: 'public',
|
||||
category: 'entertainment'
|
||||
});
|
||||
|
||||
console.log('Created video:', newVideo.data.file_key);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. User Profiles
|
||||
|
||||
### Get Current User Profile
|
||||
|
||||
```javascript
|
||||
const myProfile = await api.getMyProfile();
|
||||
console.log('My profile:', myProfile.data);
|
||||
```
|
||||
|
||||
### Get Another User's Profile
|
||||
|
||||
```javascript
|
||||
const userProfile = await api.getUserProfile(123);
|
||||
console.log('User:', userProfile.data);
|
||||
```
|
||||
|
||||
### Update Profile
|
||||
|
||||
```javascript
|
||||
await api.updateProfile({
|
||||
usr_dname: 'My New Name',
|
||||
usr_about: 'I love making videos!'
|
||||
});
|
||||
|
||||
console.log('Profile updated!');
|
||||
```
|
||||
|
||||
### Upload Avatar
|
||||
|
||||
```html
|
||||
<input type="file" id="avatar-input" accept="image/*">
|
||||
<button onclick="uploadAvatar()">Upload</button>
|
||||
|
||||
<script>
|
||||
async function uploadAvatar() {
|
||||
const fileInput = document.getElementById('avatar-input');
|
||||
const file = fileInput.files[0];
|
||||
|
||||
if (file) {
|
||||
const result = await api.uploadAvatar(file);
|
||||
console.log('Avatar uploaded:', result.data.avatar_url);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Comments
|
||||
|
||||
### Get Comments for a Video
|
||||
|
||||
```javascript
|
||||
const comments = await api.getComments('123456', {
|
||||
page: 1,
|
||||
sort: 'recent'
|
||||
});
|
||||
|
||||
console.log('Comments:', comments.data.comments);
|
||||
```
|
||||
|
||||
### Post a Comment
|
||||
|
||||
```javascript
|
||||
const newComment = await api.createComment(
|
||||
'123456', // video file_key
|
||||
'Great video!', // comment text
|
||||
null // parent_id (null for top-level)
|
||||
);
|
||||
|
||||
console.log('Comment posted:', newComment.data);
|
||||
```
|
||||
|
||||
### Reply to a Comment
|
||||
|
||||
```javascript
|
||||
const reply = await api.createComment(
|
||||
'123456', // video file_key
|
||||
'Thanks!', // reply text
|
||||
789 // parent comment ID
|
||||
);
|
||||
|
||||
console.log('Reply posted:', reply.data);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Subscriptions
|
||||
|
||||
### Subscribe to a Channel
|
||||
|
||||
```javascript
|
||||
await api.subscribe(456); // channel user ID
|
||||
console.log('Subscribed!');
|
||||
```
|
||||
|
||||
### Check if Subscribed
|
||||
|
||||
```javascript
|
||||
const status = await api.checkSubscription(456);
|
||||
|
||||
if (status.data.is_subscribed) {
|
||||
console.log('Already subscribed');
|
||||
} else {
|
||||
console.log('Not subscribed');
|
||||
}
|
||||
```
|
||||
|
||||
### Get Subscription Feed
|
||||
|
||||
```javascript
|
||||
const feed = await api.getSubscriptionFeed({ page: 1 });
|
||||
console.log('New videos from subscriptions:', feed.data.videos);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Complete Examples
|
||||
|
||||
### Video Player Page
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Watch Video</title>
|
||||
<style>
|
||||
.video-container { max-width: 800px; margin: 0 auto; }
|
||||
.actions button { margin: 5px; }
|
||||
.comments { margin-top: 20px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="video-container">
|
||||
<h1 id="video-title"></h1>
|
||||
<p id="video-description"></p>
|
||||
|
||||
<div class="actions">
|
||||
<button id="like-btn">👍 Like</button>
|
||||
<button id="subscribe-btn">Subscribe</button>
|
||||
<button id="watch-later-btn">⏰ Watch Later</button>
|
||||
</div>
|
||||
|
||||
<div class="comments">
|
||||
<h2>Comments</h2>
|
||||
<div id="comments-list"></div>
|
||||
|
||||
<form id="comment-form">
|
||||
<textarea id="comment-text" placeholder="Add a comment..." required></textarea>
|
||||
<button type="submit">Post Comment</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/f_scripts/fe/js/api-helper.js"></script>
|
||||
<script>
|
||||
const fileKey = '123456'; // Get from URL
|
||||
|
||||
// Load video and comments
|
||||
async function init() {
|
||||
try {
|
||||
// Load video
|
||||
const video = await api.getVideo(fileKey);
|
||||
document.getElementById('video-title').textContent = video.data.file_title;
|
||||
document.getElementById('video-description').textContent = video.data.file_description;
|
||||
|
||||
// Record view
|
||||
await api.recordVideoView(fileKey);
|
||||
|
||||
// Load comments
|
||||
await loadComments();
|
||||
|
||||
// Setup buttons
|
||||
setupButtons(video.data);
|
||||
|
||||
} catch (error) {
|
||||
api.handleError(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadComments() {
|
||||
const comments = await api.getComments(fileKey);
|
||||
const list = document.getElementById('comments-list');
|
||||
|
||||
list.innerHTML = comments.data.comments.map(c => `
|
||||
<div class="comment">
|
||||
<strong>${c.usr_dname}</strong>
|
||||
<p>${c.comment_text}</p>
|
||||
<small>${c.comment_date}</small>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
function setupButtons(video) {
|
||||
// Like button
|
||||
document.getElementById('like-btn').addEventListener('click', async () => {
|
||||
await api.likeVideo(fileKey, 'like');
|
||||
alert('Video liked!');
|
||||
});
|
||||
|
||||
// Subscribe button
|
||||
document.getElementById('subscribe-btn').addEventListener('click', async () => {
|
||||
await api.subscribe(video.usr_id);
|
||||
alert('Subscribed!');
|
||||
});
|
||||
|
||||
// Watch later button
|
||||
document.getElementById('watch-later-btn').addEventListener('click', async () => {
|
||||
await api.toggleWatchLater(fileKey);
|
||||
alert('Added to Watch Later!');
|
||||
});
|
||||
|
||||
// Comment form
|
||||
document.getElementById('comment-form').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const text = document.getElementById('comment-text').value;
|
||||
|
||||
await api.createComment(fileKey, text);
|
||||
document.getElementById('comment-text').value = '';
|
||||
await loadComments();
|
||||
});
|
||||
}
|
||||
|
||||
// Start
|
||||
init();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### Browse Videos Page
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Browse Videos</title>
|
||||
<style>
|
||||
.video-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; }
|
||||
.video-card { border: 1px solid #ddd; padding: 15px; }
|
||||
.filters { margin-bottom: 20px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Browse Videos</h1>
|
||||
|
||||
<div class="filters">
|
||||
<select id="sort-select">
|
||||
<option value="recent">Recent</option>
|
||||
<option value="popular">Popular</option>
|
||||
<option value="featured">Featured</option>
|
||||
</select>
|
||||
|
||||
<input type="text" id="search-input" placeholder="Search videos...">
|
||||
<button id="search-btn">Search</button>
|
||||
</div>
|
||||
|
||||
<div class="video-grid" id="video-grid"></div>
|
||||
|
||||
<button id="load-more">Load More</button>
|
||||
</div>
|
||||
|
||||
<script src="/f_scripts/fe/js/api-helper.js"></script>
|
||||
<script>
|
||||
let currentPage = 1;
|
||||
let currentSort = 'recent';
|
||||
let searchQuery = '';
|
||||
|
||||
async function loadVideos(append = false) {
|
||||
try {
|
||||
let result;
|
||||
|
||||
if (searchQuery) {
|
||||
result = await api.searchVideos(searchQuery, {
|
||||
page: currentPage,
|
||||
limit: 20
|
||||
});
|
||||
} else {
|
||||
result = await api.getVideos({
|
||||
page: currentPage,
|
||||
limit: 20,
|
||||
sort: currentSort
|
||||
});
|
||||
}
|
||||
|
||||
displayVideos(result.data.videos, append);
|
||||
|
||||
// Show/hide load more button
|
||||
const loadMoreBtn = document.getElementById('load-more');
|
||||
if (currentPage >= result.data.pagination.pages) {
|
||||
loadMoreBtn.style.display = 'none';
|
||||
} else {
|
||||
loadMoreBtn.style.display = 'block';
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
api.handleError(error);
|
||||
}
|
||||
}
|
||||
|
||||
function displayVideos(videos, append = false) {
|
||||
const grid = document.getElementById('video-grid');
|
||||
|
||||
if (!append) {
|
||||
grid.innerHTML = '';
|
||||
}
|
||||
|
||||
videos.forEach(video => {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'video-card';
|
||||
card.innerHTML = `
|
||||
<h3>${video.file_title}</h3>
|
||||
<p>${video.file_description}</p>
|
||||
<p>
|
||||
<small>
|
||||
By ${video.usr_dname} •
|
||||
${video.file_views} views •
|
||||
${video.like_count} likes
|
||||
</small>
|
||||
</p>
|
||||
<button onclick="watchVideo('${video.file_key}')">
|
||||
Watch
|
||||
</button>
|
||||
`;
|
||||
grid.appendChild(card);
|
||||
});
|
||||
}
|
||||
|
||||
window.watchVideo = function(fileKey) {
|
||||
window.location.href = `/watch.php?v=${fileKey}`;
|
||||
};
|
||||
|
||||
// Sort change
|
||||
document.getElementById('sort-select').addEventListener('change', (e) => {
|
||||
currentSort = e.target.value;
|
||||
currentPage = 1;
|
||||
loadVideos(false);
|
||||
});
|
||||
|
||||
// Search
|
||||
document.getElementById('search-btn').addEventListener('click', () => {
|
||||
searchQuery = document.getElementById('search-input').value;
|
||||
currentPage = 1;
|
||||
loadVideos(false);
|
||||
});
|
||||
|
||||
// Load more
|
||||
document.getElementById('load-more').addEventListener('click', () => {
|
||||
currentPage++;
|
||||
loadVideos(true);
|
||||
});
|
||||
|
||||
// Initial load
|
||||
loadVideos();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Error Handling
|
||||
|
||||
Always wrap API calls in try-catch:
|
||||
|
||||
```javascript
|
||||
try {
|
||||
const result = await api.someAPICall();
|
||||
// Handle success
|
||||
} catch (error) {
|
||||
// Handle error
|
||||
console.error('Error:', error.message);
|
||||
|
||||
// Use built-in error handler
|
||||
api.handleError(error);
|
||||
|
||||
// Or custom handling
|
||||
if (error.message.includes('Authentication')) {
|
||||
window.location.href = '/signin';
|
||||
} else {
|
||||
alert('Error: ' + error.message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Common Patterns
|
||||
|
||||
### Loading State
|
||||
|
||||
```javascript
|
||||
async function loadData() {
|
||||
showLoading(); // Your loading function
|
||||
|
||||
try {
|
||||
const result = await api.getVideos();
|
||||
displayData(result.data);
|
||||
} catch (error) {
|
||||
api.handleError(error);
|
||||
} finally {
|
||||
hideLoading(); // Always hide loading
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Infinite Scroll
|
||||
|
||||
```javascript
|
||||
let currentPage = 1;
|
||||
let loading = false;
|
||||
|
||||
window.addEventListener('scroll', async () => {
|
||||
if (loading) return;
|
||||
|
||||
const scrolledToBottom =
|
||||
window.innerHeight + window.scrollY >= document.body.offsetHeight - 500;
|
||||
|
||||
if (scrolledToBottom) {
|
||||
loading = true;
|
||||
currentPage++;
|
||||
|
||||
try {
|
||||
const result = await api.getVideos({ page: currentPage });
|
||||
appendVideos(result.data.videos);
|
||||
} catch (error) {
|
||||
api.handleError(error);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Debounced Search
|
||||
|
||||
```javascript
|
||||
let searchTimeout;
|
||||
|
||||
document.getElementById('search-input').addEventListener('input', (e) => {
|
||||
clearTimeout(searchTimeout);
|
||||
|
||||
searchTimeout = setTimeout(async () => {
|
||||
const query = e.target.value;
|
||||
|
||||
if (query.length >= 2) {
|
||||
const results = await api.searchVideos(query);
|
||||
displaySearchResults(results.data.videos);
|
||||
}
|
||||
}, 300); // Wait 300ms after user stops typing
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Browser Console Testing
|
||||
|
||||
Test API calls directly in the browser console:
|
||||
|
||||
```javascript
|
||||
// Check authentication
|
||||
api.isAuthenticated()
|
||||
|
||||
// Login
|
||||
await api.login('username', 'password')
|
||||
|
||||
// Get videos
|
||||
await api.getVideos({ page: 1 })
|
||||
|
||||
// Get current user
|
||||
await api.getMyProfile()
|
||||
|
||||
// View stored token
|
||||
localStorage.getItem('jwt_token')
|
||||
|
||||
// Clear token
|
||||
api.clearToken()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Next Steps
|
||||
|
||||
- Read [API_DOCUMENTATION.md](API_DOCUMENTATION.md) for complete reference
|
||||
- Check [FRONTEND_BACKEND_INTEGRATION_GUIDE.md](FRONTEND_BACKEND_INTEGRATION_GUIDE.md) for advanced patterns
|
||||
- Review example code in [f_scripts/fe/js/api-helper.js](../f_scripts/fe/js/api-helper.js)
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
If you encounter issues:
|
||||
|
||||
1. Check browser console for errors
|
||||
2. Check Network tab to see API requests/responses
|
||||
3. Verify you're logged in: `api.isAuthenticated()`
|
||||
4. Check API documentation for correct parameters
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** January 2025
|
||||
Reference in New Issue
Block a user