mirror of
https://github.com/mediacms-io/mediacms.git
synced 2025-12-08 21:22:30 -05:00
Compare commits
7 Commits
feat-docke
...
feat/ui-tr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c367d7eeb | ||
|
|
6eae3310ad | ||
|
|
e536c74576 | ||
|
|
aeef8284bf | ||
|
|
a90fcbf8dd | ||
|
|
1b3cdfd302 | ||
|
|
cd7dd4f72c |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -35,3 +35,4 @@ frontend-tools/video-editor/client/public/videos/sample-video.mp3
|
||||
frontend-tools/chapters-editor/client/public/videos/sample-video.mp3
|
||||
static/chapters_editor/videos/sample-video.mp3
|
||||
static/video_editor/videos/sample-video.mp3
|
||||
templates/todo-MS4.md
|
||||
|
||||
@@ -69,7 +69,7 @@ Copyright Markos Gogoulos.
|
||||
|
||||
## Support and paid services
|
||||
|
||||
We provide custom installations, development of extra functionality, migration from existing systems, integrations with legacy systems, training and support. Contact us at info@mediacms.io for more information.
|
||||
We provide custom installations, development of extra functionality, migration from existing systems, integrations with legacy systems, training and support. Checkout our [services page](https://mediacms.io/#services/) for more information.
|
||||
|
||||
### Commercial Hostings
|
||||
**Elestio**
|
||||
|
||||
@@ -1 +1 @@
|
||||
VERSION = "7.2.1"
|
||||
VERSION = "7.2.2"
|
||||
|
||||
@@ -329,10 +329,17 @@ class Media(models.Model):
|
||||
|
||||
if to_transcribe:
|
||||
TranscriptionRequest.objects.create(media=self, translate_to_english=False)
|
||||
tasks.whisper_transcribe.delay(self.friendly_token, translate_to_english=False)
|
||||
tasks.whisper_transcribe.apply_async(
|
||||
args=[self.friendly_token, False],
|
||||
countdown=10,
|
||||
)
|
||||
|
||||
if to_transcribe_and_translate:
|
||||
TranscriptionRequest.objects.create(media=self, translate_to_english=True)
|
||||
tasks.whisper_transcribe.delay(self.friendly_token, translate_to_english=True)
|
||||
tasks.whisper_transcribe.apply_async(
|
||||
args=[self.friendly_token, True],
|
||||
countdown=10,
|
||||
)
|
||||
|
||||
def update_search_vector(self):
|
||||
"""
|
||||
|
||||
@@ -150,6 +150,11 @@ const App = () => {
|
||||
canRedo={historyPosition < history.length - 1}
|
||||
/>
|
||||
|
||||
{/* Timeline Header */}
|
||||
<div className="timeline-header-container">
|
||||
<h2 className="timeline-header-title">Add Chapters</h2>
|
||||
</div>
|
||||
|
||||
{/* Timeline Controls */}
|
||||
<TimelineControls
|
||||
currentTime={currentTime}
|
||||
|
||||
@@ -20,7 +20,7 @@ const useVideoChapters = () => {
|
||||
// Sort by start time to find chronological position
|
||||
const sortedSegments = allSegments.sort((a, b) => a.startTime - b.startTime);
|
||||
// Find the index of our new segment
|
||||
const chapterIndex = sortedSegments.findIndex(seg => seg.startTime === newSegmentStartTime);
|
||||
const chapterIndex = sortedSegments.findIndex((seg) => seg.startTime === newSegmentStartTime);
|
||||
return `Chapter ${chapterIndex + 1}`;
|
||||
};
|
||||
|
||||
@@ -30,10 +30,16 @@ const useVideoChapters = () => {
|
||||
const sortedSegments = [...segments].sort((a, b) => a.startTime - b.startTime);
|
||||
|
||||
// Renumber each segment based on its chronological position
|
||||
return sortedSegments.map((segment, index) => ({
|
||||
...segment,
|
||||
chapterTitle: `Chapter ${index + 1}`
|
||||
}));
|
||||
// Only update titles that follow the default "Chapter X" pattern to preserve custom titles
|
||||
return sortedSegments.map((segment, index) => {
|
||||
const currentTitle = segment.chapterTitle || '';
|
||||
const isDefaultTitle = /^Chapter \d+$/.test(currentTitle);
|
||||
|
||||
return {
|
||||
...segment,
|
||||
chapterTitle: isDefaultTitle ? `Chapter ${index + 1}` : currentTitle,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// Helper function to parse time string (HH:MM:SS.mmm) to seconds
|
||||
@@ -124,9 +130,7 @@ const useVideoChapters = () => {
|
||||
let initialSegments: Segment[] = [];
|
||||
|
||||
// Check if we have existing chapters from the backend
|
||||
const existingChapters =
|
||||
(typeof window !== 'undefined' && (window as any).MEDIA_DATA?.chapters) ||
|
||||
[];
|
||||
const existingChapters = (typeof window !== 'undefined' && (window as any).MEDIA_DATA?.chapters) || [];
|
||||
|
||||
if (existingChapters.length > 0) {
|
||||
// Create segments from existing chapters
|
||||
@@ -564,8 +568,11 @@ const useVideoChapters = () => {
|
||||
`Updating segments with action: ${actionType}, recordHistory: ${isSignificantChange ? 'true' : 'false'}`
|
||||
);
|
||||
|
||||
// Renumber all segments to ensure proper chronological naming
|
||||
const renumberedSegments = renumberAllSegments(e.detail.segments);
|
||||
|
||||
// Update segment state immediately for UI feedback
|
||||
setClipSegments(e.detail.segments);
|
||||
setClipSegments(renumberedSegments);
|
||||
|
||||
// Always save state to history for non-intermediate actions
|
||||
if (isSignificantChange) {
|
||||
@@ -573,7 +580,7 @@ const useVideoChapters = () => {
|
||||
// ensure we capture the state properly
|
||||
setTimeout(() => {
|
||||
// Deep clone to ensure state is captured correctly
|
||||
const segmentsClone = JSON.parse(JSON.stringify(e.detail.segments));
|
||||
const segmentsClone = JSON.parse(JSON.stringify(renumberedSegments));
|
||||
|
||||
// Create a complete state snapshot
|
||||
const stateWithAction: EditorState = {
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
#chapters-editor-root {
|
||||
.timeline-header-container {
|
||||
margin-left: 1rem;
|
||||
margin-top: -0.5rem;
|
||||
}
|
||||
|
||||
.timeline-header-title {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
color: #059669;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.timeline-container-card {
|
||||
background-color: white;
|
||||
border-radius: 0.5rem;
|
||||
@@ -11,6 +23,8 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 2px solid rgba(16, 185, 129, 0.2);
|
||||
}
|
||||
|
||||
.timeline-title {
|
||||
@@ -21,6 +35,8 @@
|
||||
|
||||
.timeline-title-text {
|
||||
font-weight: 700;
|
||||
color: #059669;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.current-time {
|
||||
@@ -48,10 +64,11 @@
|
||||
.timeline-container {
|
||||
position: relative;
|
||||
min-width: 100%;
|
||||
background-color: #fafbfc;
|
||||
background-color: #E2EDE4;
|
||||
height: 70px;
|
||||
border-radius: 0.25rem;
|
||||
overflow: visible !important;
|
||||
border: 1px solid rgba(16, 185, 129, 0.2);
|
||||
}
|
||||
|
||||
.timeline-marker {
|
||||
@@ -194,7 +211,7 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 0.4rem;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
background-color: rgba(16, 185, 129, 0.6);
|
||||
color: white;
|
||||
opacity: 1;
|
||||
transition: background-color 0.2s;
|
||||
@@ -202,15 +219,15 @@
|
||||
}
|
||||
|
||||
.clip-segment:hover .clip-segment-info {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
background-color: rgba(16, 185, 129, 0.7);
|
||||
}
|
||||
|
||||
.clip-segment.selected .clip-segment-info {
|
||||
background-color: rgba(59, 130, 246, 0.5);
|
||||
background-color: rgba(5, 150, 105, 0.8);
|
||||
}
|
||||
|
||||
.clip-segment.selected:hover .clip-segment-info {
|
||||
background-color: rgba(59, 130, 246, 0.4);
|
||||
background-color: rgba(5, 150, 105, 0.75);
|
||||
}
|
||||
|
||||
.clip-segment-name {
|
||||
|
||||
@@ -309,6 +309,11 @@ const App = () => {
|
||||
canRedo={historyPosition < history.length - 1}
|
||||
/>
|
||||
|
||||
{/* Timeline Header */}
|
||||
<div className="timeline-header-container">
|
||||
<h2 className="timeline-header-title">Trim or Split</h2>
|
||||
</div>
|
||||
|
||||
{/* Timeline Controls */}
|
||||
<TimelineControls
|
||||
currentTime={currentTime}
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
#video-editor-trim-root {
|
||||
.timeline-header-container {
|
||||
margin-left: 1rem;
|
||||
margin-top: -0.5rem;
|
||||
}
|
||||
|
||||
.timeline-header-title {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
color: #2563eb;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.timeline-container-card {
|
||||
background-color: white;
|
||||
border-radius: 0.5rem;
|
||||
@@ -11,6 +23,8 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 2px solid rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
|
||||
.timeline-title {
|
||||
@@ -21,6 +35,8 @@
|
||||
|
||||
.timeline-title-text {
|
||||
font-weight: 700;
|
||||
color: #2563eb;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.current-time {
|
||||
@@ -48,10 +64,11 @@
|
||||
.timeline-container {
|
||||
position: relative;
|
||||
min-width: 100%;
|
||||
background-color: #fafbfc;
|
||||
background-color: #eff6ff;
|
||||
height: 70px;
|
||||
border-radius: 0.25rem;
|
||||
overflow: visible !important;
|
||||
border: 1px solid rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
|
||||
.timeline-marker {
|
||||
@@ -194,7 +211,7 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 0.4rem;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
background-color: rgba(59, 130, 246, 0.6);
|
||||
color: white;
|
||||
opacity: 1;
|
||||
transition: background-color 0.2s;
|
||||
@@ -202,15 +219,15 @@
|
||||
}
|
||||
|
||||
.clip-segment:hover .clip-segment-info {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
background-color: rgba(59, 130, 246, 0.7);
|
||||
}
|
||||
|
||||
.clip-segment.selected .clip-segment-info {
|
||||
background-color: rgba(59, 130, 246, 0.5);
|
||||
background-color: rgba(37, 99, 235, 0.8);
|
||||
}
|
||||
|
||||
.clip-segment.selected:hover .clip-segment-info {
|
||||
background-color: rgba(59, 130, 246, 0.4);
|
||||
background-color: rgba(37, 99, 235, 0.75);
|
||||
}
|
||||
|
||||
.clip-segment-name {
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user