Compare commits

..

No commits in common. "master" and "pre-1.6-merge-20260819" have entirely different histories.

34 changed files with 699 additions and 1123 deletions

2
.gitignore vendored
View file

@ -10,4 +10,4 @@
!/vendor/iced_video_player/
!/vendor/iced_video_player/**
/vendor.tar
.idea/
.idea/

1422
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[package]
name = "cosmic-player"
version = "1.8.0"
version = "1.3.0"
edition = "2024"
rust-version = "1.93"

36
debian/changelog vendored
View file

@ -1,39 +1,3 @@
cosmic-player (1.8.0) noble; urgency=medium
* Epoch 1.8.0 version update
-- Jeremy Soller <jeremy@system76.com> Wed, 02 Sep 2026 08:15:23 -0600
cosmic-player (1.7.0) noble; urgency=medium
* Epoch 1.7.0 version update
-- Jeremy Soller <jeremy@system76.com> Tue, 25 Aug 2026 09:20:51 -0600
cosmic-player (1.6.0) noble; urgency=medium
* Epoch 1.6.0 version update
-- Jeremy Soller <jeremy@system76.com> Tue, 18 Aug 2026 07:14:20 -0600
cosmic-player (1.5.0) noble; urgency=medium
* Epoch 1.5.0 version update
-- Jeremy Soller <jeremy@system76.com> Tue, 28 Jul 2026 09:14:34 -0600
cosmic-player (1.4.0) noble; urgency=medium
* Epoch 1.4.0 version update
-- Jeremy Soller <jeremy@system76.com> Mon, 20 Jul 2026 14:36:34 -0600
cosmic-player (1.3.0) noble; urgency=medium
* Epoch 1.3.0 version update
-- Jeremy Soller <jeremy@system76.com> Sun, 12 Jul 2026 13:21:52 -0600
cosmic-player (1.2.0) noble; urgency=medium
* Epoch 1.2.0 version update

View file

@ -17,7 +17,7 @@ settings = الإعدادات
### Appearance
appearance = المظهر
theme = السمة
theme = النسق
match-desktop = طابق سطح المكتب
dark = داكن
light = فاتح
@ -34,7 +34,7 @@ close-file = أغلِق الملف
open-media-folder = افتح مجلَّد الوسائط...
open-recent-media-folder = افتح مجلِّد الوسائط الحديثة
close-media-folder = أغلِق مجلد الوسائط
quit = إنهِ
quit = أنهِ
off = معطّل
clear-recent = امحُ القائمة الحديثة
repeat-disabled = التكرار مُعطَّل

View file

@ -1,3 +1,2 @@
quit = Surt
settings = Configuració
open-file = Obre el fitxer

View file

@ -1,36 +0,0 @@
quit = Afslut
appearance = Udseende
theme = Tema
match-desktop = Følg skrivebordets indstillinger
dark = Mørkt
light = Lyst
close-file = Luk fil
settings = Indstillinger
open-file = Åbn fil
open-folder = Åbn mappe
file = Fil
album = Album: { $album }
audio = Lyd
no-video-or-audio-file-open = Ingen video- eller lydfil åben
off = Fra
subtitles = Undertekster
unknown-author = Ukendt Forfatter
untitled = Utituleret
open-media = Åbn medie...
open-recent-media = Åbn seneste medie
clear-recent = Ryd seneste-liste
open-media-folder = Åbn mediemappe...
open-recent-media-folder = Åbn seneste mediemappe
close-media-folder = Luk mediemappe
repeat-disabled = Gentagelse deaktiveret
repeat-track = Gentag nummer
playback = Afspilning
next-frame = Næste billede
previous-frame = Forrige billede
ab-repeat = A-B-gentagelse
ab-repeat-set-a = A-B-gentagelse (A)
ab-repeat-set-b = A-B-gentagelse (B)
ab-repeat-clear = Ryd A-B-gentagelse
xdg-name = COSMIC Medieafspiller
xdg-comment = Medieafspiller til COSMIC-skrivebordet
xdg-keywords = Lyd;Film;Film;Musik;Lyd;Video;

View file

@ -28,10 +28,10 @@ light = Hell
## Datei
file = Datei
open-media = Medium öffnen ...
open-media = Medium öffnen...
open-recent-media = Zuletzt verwendetes Medium öffnen
close-file = Datei schließen
open-media-folder = Medienordner öffnen ...
open-media-folder = Medienordner öffnen...
open-recent-media-folder = Ordner der zuletzt verwendeten Medien öffnen
close-media-folder = Medienordner schließen
quit = Beenden

View file

@ -10,7 +10,7 @@ untitled = Χωρίς τίτλο
theme = Θέμα
repeat-track = Επανάληψη κομματιού
appearance = Εμφάνιση
quit = Τερματισμός
quit = Έξοδος
album = Άλμπουμ: { $album }
playback = Αναπαραγωγή
next-frame = Επόμενο καρέ

View file

@ -1,11 +0,0 @@
quit = Quit
settings = Settings
appearance = Appearance
theme = Theme
match-desktop = Match desktop
dark = Dark
light = Light
file = File
close-file = Close file
open-file = Open file
open-folder = Open folder

View file

@ -1 +0,0 @@
quit = خروج

View file

@ -11,7 +11,7 @@ unknown-author = Tuntematon tekijä
untitled = Nimetön
appearance = Ulkoasu
theme = Teema
match-desktop = Sama kuin työpöytä
match-desktop = Sovita työpöytään
dark = Tumma
light = Vaalea
file = Tiedosto

View file

@ -2,7 +2,7 @@ album = Album : { $album }
audio = Audio
no-video-or-audio-file-open = Aucun média sélectionné
open-file = Ouvrir le fichier
open-folder = Ouvrir le dossier
open-folder = Ouvrir dossier
subtitles = Sous-titres
unknown-author = Auteur Inconnu
untitled = Sans titre

View file

@ -1,36 +0,0 @@
settings = Axustes
appearance = Apariencia
theme = Tema
match-desktop = Axustar ao escritorio
dark = Escuro
light = Claro
file = Ficheiro
close-file = Pechar ficheiro
quit = Pechar
open-file = Abrir ficheiro
open-folder = Abrir cartafol
album = Álbum: { $album }
audio = Audio
no-video-or-audio-file-open = Non hai ningún ficheiro de vídeo ou audio aberto
off = Desactivado
subtitles = Subtitulos
unknown-author = Autor descoñecido
untitled = Sen titulo
open-media = Abrir multimedia..
open-recent-media = Abrir multimedia recente
clear-recent = Limpar lista de recentes
open-media-folder = Abrir cartafol multimedia...
open-recent-media-folder = Abrir o cartafol de medios recentes
close-media-folder = Pechar carpeta multimedia
repeat-disabled = Repetición desactivada
repeat-track = Repetir pista
playback = Reprodución
next-frame = Seguinte fotograma
previous-frame = Fotograma anterior
ab-repeat = Repetición A-B
ab-repeat-set-a = Repetición A-B (A)
ab-repeat-set-b = Repetición A-B (B)
ab-repeat-clear = Limpar repetición A-B
xdg-name = Reproductor Multimedia COSMIC
xdg-comment = Reprodutor multimedia para o escritorio COSMIC
xdg-keywords = Audio;Película;Filme;Música;Son;Vídeo;

View file

@ -3,13 +3,13 @@
## Settings
settings = सेटिंग्स
settings = सेटिंग
### Appearance
appearance = रुप-रंग
theme = प्रसंग
match-desktop = डेस्कटॉप से मेल खाएँ
appearance = उपस्थिति
theme = थीम
match-desktop = डेस्कटॉप से मिलाएं
dark = डार्क
light = लाइट
album = एल्बम: { $album }
@ -24,7 +24,7 @@ close-file = फ़ाइल बंद करें
open-media-folder = मीडिया फ़ोल्डर खोलें…
open-recent-media-folder = हाल का मीडिया फ़ोल्डर खोलें
close-media-folder = मीडिया फ़ोल्डर बंद करें
quit = बाहर जाएँ
quit = बंद करें
no-video-or-audio-file-open = कोई वीडियो या ऑडियो फ़ाइल खुली नहीं है
open-media = मीडिया खोलें…
open-recent-media = हाल का मीडिया खोलें

View file

@ -11,7 +11,7 @@ settings = Pengaturan
untitled = Tidak Berjudul
appearance = Tampilan
theme = Tema
match-desktop = Cocok desktop
match-desktop = Cocokkan desktop
dark = Gelap
light = Terang
file = Berkas
@ -31,6 +31,3 @@ ab-repeat = Pengulangan A-B
playback = Pemutaran
next-frame = Bagian Selanjutnya
previous-frame = Bagian Sebelumnya
xdg-name = Pemutar Media COSMIC
xdg-comment = Pemutar Media untuk desktop COSMIC
xdg-keywords = Audio;Film;Film;Musik;Suara;Video;

View file

@ -18,7 +18,7 @@ settings = Impostazioni
appearance = Aspetto
theme = Tema
match-desktop = Abbina al desktop
match-desktop = Sistema
dark = Scuro
light = Chiaro

View file

@ -2,13 +2,13 @@ open-file = Ldi afaylu
open-folder = Ldi akaram
repeat-disabled = Allus yensa
repeat-track = Ales tazribt
quit = Ffeɣ
quit = Tuffɣa
settings = Iɣewwaṛen
appearance = Timeẓri
theme = Asentel
dark = Aɣmayan
light = Aceɛlal
match-desktop = Semṣada akked tnarit
match-desktop = Amṣada d tnarit
file = Afaylu
close-file = Mdel afaylu
album = Album: { $album }

View file

@ -1,11 +1,11 @@
open-file = Файлды ашу
open-folder = Буманы ашу
settings = Баптаулар
appearance = Сыртқы көрініс
theme = Тема
match-desktop = Жұмыс үстеліне сәйкес болу
dark = Қою
light = Ашық
appearance = Сыртқы түрі
theme = Тақырып
match-desktop = Жұмыс үстеліне сәйкес келу
dark = Қараңғы
light = Жарық
file = Файл
quit = Шығу
close-file = Файлды жабу

View file

@ -1,36 +0,0 @@
settings = Sazkarî
appearance = Xuyang
theme = Rûkar
dark = Tarî
light = Ronî
quit = Biqedîne
open-file = Pelê veke
open-folder = Peldankê veke
match-desktop = Bi sermaseyê re li hev bîne
album = Elbum: { $album }
audio = Deng
no-video-or-audio-file-open = Tu pelê vîdiyo yan jî dengê vekirî tune ne
off = Vemirî
subtitles = Jêrnivîs
unknown-author = Hunermendê nenas
untitled = Bê sernav
file = Pel
open-media = Mediyayê veke...
close-file = Pelê bigire
ab-repeat = A-B dûbare bike
open-recent-media = Mediyayên dawî veke
clear-recent = Lîsteya dawî pak bike
open-media-folder = Peldanka mediyayê veke...
open-recent-media-folder = Peldanka mediyayê ya dawî veke
close-media-folder = Peldanka mediyayê bigire
repeat-disabled = Dûbarekirin neçalak e
repeat-track = Stranê dûbare bike
playback = Lîstin
next-frame = Wêneya pêş
previous-frame = Wêneya paş
ab-repeat-set-a = A-B (A) dûbare bike
ab-repeat-set-b = A-B (B) dûbare bike
ab-repeat-clear = Dûbarekirina A-B pak bike
xdg-name = Lîstikvanê mediyayê ya COSMIC
xdg-comment = Lîstikvanê mediyayê ji bo sermaseya COSMIC
xdg-keywords = Deng;Fîlm;Fîlm;Muzîk;Deng;Vîdiyo;

View file

@ -25,11 +25,3 @@ close-file = 파일 닫기
repeat-disabled = 반복 비활성화
previous-frame = 이전 프레임
next-frame = 다음 프레임
ab-repeat = A-B 반복
ab-repeat-set-a = A-B 반복 (A)
ab-repeat-set-b = A-B 반복 (B)
ab-repeat-clear = A-B 반복 취소
xdg-name = COSMIC 미디어 플레이어
xdg-comment = COSMIC 데스크톱을 위한 미디어 플레이어
playback = 재생
repeat-track = 트랙 반복

View file

@ -1,5 +1,5 @@
quit = Išjungti
settings = Nuostatai
quit = Išeiti
settings = Nuostatos
close-file = Uždaryti failą
match-desktop = Pagal darbalaukio temą
appearance = Išvaizda

View file

@ -1,34 +1 @@
settings = Configuracion
album = Album : { $album }
audio = Àudio
no-video-or-audio-file-open = Cap de fichièr vidèo o àudio pas dobèrt
off = Desactivat
subtitles = Sostítols
unknown-author = Autor desconegut
untitled = Sens títol
appearance = Aparéncia
theme = Tèma
match-desktop = Assortir al burèu
dark = Escur
light = Clar
file = Fichièr
open-media = Dorbir mèdia...
open-recent-media = Dobrir un mèdia recent
clear-recent = Escafar la lista recenta
close-file = Tampar lo fichièr
open-media-folder = Dobrir un dossièr multimèdia...
open-recent-media-folder = Dobrir lo dossièr del mèdia recent
close-media-folder = Tampar lo dossièr del mèdia
repeat-disabled = Repeticion desactivada
repeat-track = Repetir la pista
playback = Lectura
next-frame = Imatge seguent
previous-frame = Imatge precedent
ab-repeat = Repetir la seccion A-B
ab-repeat-set-a = Repeticion A-B (A)
ab-repeat-set-b = Repeticion A-B (B)
ab-repeat-clear = Escafar la repeticion A-B
xdg-name = Lector multimèdia COSMIC
xdg-comment = Lector multimèdia pel burèu COSMIC
xdg-keywords = Àudio;Musica;Vidèo;Film;Son;
quit = Quitar
settings = Paramètres

View file

@ -1,10 +1,10 @@
settings = ਸੈਟਿੰਗਾਂ
appearance = ਦਿੱਖ
theme = ਥੀਮ
match-desktop = ਡੈਸਕਟਾਪ ਨਾਲ ਮਿਲਾਓ
match-desktop = ਡੈਸਕਟਾਪ ਨਾਲ ਮਿਲਦਾ
dark = ਗੂੜ੍ਹਾ
light = ਫਿੱਕਾ
quit = ਸਮਾਪਤ
light = ਹਲਕਾ
quit = ਬਾਹਰ
open-file = ਫ਼ਾਇਲ ਨੂੰ ਖੋਲ੍ਹੋ
file = ਫ਼ਾਇਲ
open-folder = ਫੋਲਡਰ ਨੂੰ ਖੋਲ੍ਹੋ

View file

@ -34,7 +34,7 @@ close-file = Zamknij plik
open-media-folder = Otwórz katalog z nośnikami…
open-recent-media-folder = Otwórz poprzedni katalog z nośnikami
close-media-folder = Zamknij katalog z nośnikami
quit = Zakończ
quit = Zamknij
off = Wyłączone
clear-recent = Wyczyść listę poprzednich
repeat-disabled = Powtarzanie wyłączone

View file

@ -34,7 +34,7 @@ close-file = Fechar arquivo
open-media-folder = Abrir pasta de mídias...
open-recent-media-folder = Abrir pasta recente de mídias
close-media-folder = Fechar pasta de mídias
quit = Encerrar
quit = Sair
off = Desligado
clear-recent = Limpar lista de recentes
repeat-track = Repetir faixa

View file

@ -15,7 +15,7 @@ light = Светлая
open-file = Открыть файл
album = Альбом: { $album }
audio = Аудио
no-video-or-audio-file-open = Нет открытых медиафайлов
no-video-or-audio-file-open = Нет открытых видео или аудио файлов
open-folder = Открыть папку
subtitles = Субтитры
unknown-author = Неизвестный автор
@ -27,7 +27,7 @@ close-file = Закрыть файл
open-media-folder = Открыть папку медиафайлов...
open-recent-media-folder = Открыть папку недавних медиафайлов
close-media-folder = Закрыть папку медиафайлов
quit = Завершить
quit = Выйти
off = Выкл.
clear-recent = Очистить список недавних
repeat-disabled = Повтор отключён

View file

@ -1,4 +0,0 @@
settings = การตั้งค่า
dark = มืด
light = สว่าง
appearance = ลักษณะ

View file

@ -19,7 +19,7 @@ close-file = Закрити файл
open-media-folder = Відкрити теку...
open-recent-media-folder = Відкрити попередню теку
close-media-folder = Закрити теку
quit = Завершити
quit = Вийти
off = Вимк.
clear-recent = Очистити список
repeat-disabled = Не повторювати

View file

@ -34,7 +34,7 @@ close-file = 關閉檔案
open-media-folder = 開啟媒體資料夾...
open-recent-media-folder = 開啟最近使用的媒體資料夾
close-media-folder = 關閉媒體資料夾
quit = 結束
quit = 退出
off = 關閉
clear-recent = 清除最近使用清單
repeat-disabled = 重複已經停用

View file

@ -82,32 +82,7 @@ fn get_framerate(video: &Video) -> Option<f64> {
let caps = pad.current_caps()?;
let structure = caps.structure(0)?;
let framerate = structure.get::<gst::Fraction>("framerate").ok()?;
positive_framerate(framerate)
}
fn positive_framerate(framerate: gst::Fraction) -> Option<f64> {
if framerate.numer() <= 0 || framerate.denom() <= 0 {
return None;
}
let fps = framerate.numer() as f64 / framerate.denom() as f64;
fps.is_finite().then_some(fps)
}
#[cfg(test)]
mod tests {
use super::positive_framerate;
use iced_video_player::gst;
#[test]
fn variable_framerate_uses_fallback() {
assert_eq!(positive_framerate(gst::Fraction::new(0, 1)), None);
}
#[test]
fn fixed_framerate_is_preserved() {
assert_eq!(positive_framerate(gst::Fraction::new(30, 1)), Some(30.0));
}
Some(framerate.numer() as f64 / framerate.denom() as f64)
}
/// Runs application with these settings
@ -926,7 +901,7 @@ impl Application for App {
/// Creates the application, and optionally emits command on initialize.
fn init(mut core: Core, flags: Self::Flags) -> (Self, Task<Self::Message>) {
core.window.border_padding = Some(0);
core.window.content_container = false;
#[cfg(feature = "xdg-portal")]
let inhibit = {
@ -1087,6 +1062,18 @@ impl Application for App {
}
}
fn style(&self) -> Option<cosmic::iced::core::theme::Style> {
// This ensures we have a solid background color even when using no content container
let theme = self.core.system_theme();
Some(cosmic::iced::core::theme::Style {
background_color: theme.cosmic().bg_color().into(),
icon_color: theme.cosmic().on_bg_color().into(),
text_color: theme.cosmic().on_bg_color().into(),
})
}
/// Handle application events here.
fn update(&mut self, message: Self::Message) -> Task<Self::Message> {
match message {
@ -1753,7 +1740,7 @@ impl Application for App {
};
let Some(video) = &self.video_opt else {
// TODO: use space variables
//TODO: use space variables
let column = widget::column::with_capacity(4)
.align_x(Alignment::Center)
.spacing(24)
@ -1770,7 +1757,11 @@ impl Application for App {
.push(widget::button::suggested(fl!("open-file")).on_press(Message::FileOpen))
.push(widget::space::vertical());
return column.into();
return widget::container(column)
.width(Length::Fill)
.height(Length::Fill)
.class(theme::Container::WindowBackground)
.into();
};
let muted = video.muted();
@ -1790,9 +1781,8 @@ impl Application for App {
let mut background_color = Color::BLACK;
let mut text_color_opt = None;
if !video.has_video() {
let bg = theme.cosmic().background(theme.transparent);
background_color = Color::TRANSPARENT;
text_color_opt = Some(Color::from(bg.on));
background_color = theme.cosmic().bg_component_color().into();
text_color_opt = Some(Color::from(theme.cosmic().on_bg_component_color()));
let mut col = widget::column::with_capacity(10);
col = col.push(widget::space::vertical());
@ -1984,7 +1974,7 @@ impl Application for App {
//TODO: move style to libcosmic
.class(theme::Container::custom(|theme| {
let cosmic = theme.cosmic();
let component = &cosmic.background(theme.transparent).component;
let component = &cosmic.background(false).component;
widget::container::Style {
icon_color: Some(component.on.into()),
text_color: Some(component.on.into()),

View file

@ -4,40 +4,6 @@ use iced_video_player::{Video, gst, gst_app, gst_pbutils};
use cosmic::action;
use cosmic::app::Task;
const SUSPICIOUS_AUDIO_GAP_DURATION: gst::ClockTime = gst::ClockTime::SECOND;
fn is_suspicious_audio_gap(duration: Option<gst::ClockTime>) -> bool {
duration.is_some_and(|duration| duration >= SUSPICIOUS_AUDIO_GAP_DURATION)
}
fn suppress_suspicious_audio_gaps(audio_filter: &gst::Element) {
let Some(src_pad) = audio_filter.static_pad("src") else {
log::warn!("audio filter has no source pad; malformed audio gaps will not be suppressed");
return;
};
let _ = src_pad.add_probe(gst::PadProbeType::EVENT_DOWNSTREAM, |_, info| {
let Some(event) = info.event() else {
return gst::PadProbeReturn::Ok;
};
let gst::EventView::Gap(gap) = event.view() else {
return gst::PadProbeReturn::Ok;
};
let (timestamp, duration) = gap.get();
if !is_suspicious_audio_gap(duration) {
return gst::PadProbeReturn::Ok;
}
// Some malformed MP4 edit lists cause streamsynchronizer to emit a
// long GAP before almost every audio buffer. The next buffer's PTS is
// still continuous, so forwarding those events makes audio sinks
// repeatedly wait or resynchronise and produces choppy playback.
log::debug!("suppressing audio GAP at {timestamp} with duration {duration:?}");
drop(info.take_event());
gst::PadProbeReturn::Handled
});
}
#[derive(Debug, Default)]
pub struct VideoSettings {
pub mute: bool,
@ -67,14 +33,11 @@ pub fn new_video(
.downcast::<gst::Pipeline>()
.map_err(|_| iced_video_player::Error::Cast)
.unwrap();
let audio_filter = gst::ElementFactory::make("scaletempo")
.build()
.unwrap_or_else(|_| {
log::warn!("scaletempo element not available; speed changes will affect pitch");
gst::ElementFactory::make("identity").build().unwrap()
});
suppress_suspicious_audio_gaps(&audio_filter);
pipeline.set_property("audio-filter", &audio_filter);
if let Ok(scaletempo) = gst::ElementFactory::make("scaletempo").build() {
pipeline.set_property("audio-filter", &scaletempo);
} else {
log::warn!("scaletempo element not available; speed changes will affect pitch");
}
pipeline.connect("element-setup", false, |vals| {
let Ok(elem) = vals[1].get::<gst::Element>() else {
return None;
@ -148,25 +111,3 @@ fn disable_broken_hardware_decoders() {
}
}
}
#[cfg(test)]
mod tests {
use super::is_suspicious_audio_gap;
use iced_video_player::gst;
#[test]
fn long_audio_gap_is_suspicious() {
assert!(is_suspicious_audio_gap(Some(gst::ClockTime::SECOND)));
assert!(is_suspicious_audio_gap(Some(
gst::ClockTime::from_seconds(3)
)));
}
#[test]
fn short_or_unknown_audio_gap_is_preserved() {
assert!(!is_suspicious_audio_gap(Some(
gst::ClockTime::from_mseconds(999)
)));
assert!(!is_suspicious_audio_gap(None));
}
}